Sentry 监控 - 全栈开发人员的分布式跟踪 101 系列教程(第一部分)
系列
- 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本
- 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps
- Sentry For React 完整接入详解
- Sentry For Vue 完整接入详解
- Sentry-CLI 使用详解
- Sentry Web 性能监控 - Web Vitals
- Sentry Web 性能监控 - Metrics
- Sentry Web 性能监控 - Trends
- Sentry Web 前端监控 - 最佳实践(官方教程)
- Sentry 后端监控 - 最佳实践(官方教程)
- Sentry 监控 - Discover 大数据查询分析引擎
- Sentry 监控 - Dashboards 数据可视化大屏
- Sentry 监控 - Environments 区分不同部署环境的事件数据
- Sentry 监控 - Security Policy 安全策略报告
- Sentry 监控 - Search 搜索查询实战
- Sentry 监控 - Alerts 告警
- Sentry 监控 - Distributed Tracing 分布式跟踪
欢迎来到我们关于全栈开发人员分布式跟踪(Distributed Tracing
)的系列的第 1
部分。在本系列中,我们将学习分布式跟踪的细节,以及它如何帮助您监控全栈应用程序日益复杂的需求。
在 Web
的早期,编写 Web
应用程序很简单。开发人员使用 PHP
等语言在服务器上生成 HTML
,与 MySQL
等单一关系数据库进行通信,大多数交互性由静态 HTML
表单组件驱动。 虽然调试工具很原始,但理解代码的执行流程很简单。
在今天的现代 web
栈中,它什么都不是。全栈开发人员需要编写在浏览器中执行的 JavaScript
,与多种数据库技术互操作,并在不同的服务器架构(例如:serverless
)上部署服务器端代码。如果没有合适的工具,了解浏览器中的用户交互如何关联到服务器堆栈深处的 500 server error
几乎是不可能的。Enter
:分布式跟踪。
我试图解释 2021
年我的 web
堆栈中的瓶颈。
分布式跟踪(Distributed tracing
)是一种监控技术,它将多个服务之间发生的操作和请求联系起来。 这允许开发人员在端到端请求从一个服务移动到另一个服务时“跟踪(trace)”
它的路径,让他们能够查明对整个系统产生负面影响的单个服务中的错误或性能瓶颈。
在这篇文章中,我们将了解有关分布式跟踪概念的更多信息,在代码中查看端到端(end-to-end
)跟踪示例,并了解如何使用跟踪元数据为您的日志记录和监控工具添加有价值的上下文。 完成后,您不仅会了解分布式跟踪的基础知识,还会了解如何应用跟踪技术来更有效地调试全栈 Web
应用程序。
但首先,让我们回到开头:什么是分布式追踪?
分布式追踪基础
分布式跟踪是一种记录多个服务的连接操作的方法。 通常,这些操作是由从一个服务到另一个服务的请求发起的,其中“请求(request)”
可以是实际的 HTTP
请求,也可以是通过任务队列或其他一些异步方式调用的工作。
跟踪由两个基本组件组成:
Span
描述发生在服务上的操作或“work”
。Span
可以描述广泛的操作——例如,响应HTTP
请求的web
服务器的操作——也可以描述单个函数的调用。trace
描述了一个或多个连接span
的端到端(end-to-end
)旅程。 如果trace
连接在多个服务上执行的span
(“work”
),则该trace
被认为是分布式跟踪。
让我们看一个假设的分布式跟踪示例。
上图说明了 trace
如何从一个服务(一个在浏览器上运行的 React
应用程序)开始,并通过调用 API Web Server
继续,甚至进一步调用后台任务 worker
。此图中的 span
是在每个服务中执行的 work
,每个 span
都可以“追溯到(traced
)”由浏览器应用程序启动的初始工作(initial work
)。 最后,由于这些操作发生在不同的服务上,因此该跟踪被认为是分布式的。
描述广泛操作的跨度(例如:响应 HTTP request
的 Web server
的完整生命周期)有时被称为事务跨度(transaction spans
),甚至只是事务。 我们将在本系列的第 2
部分中更多地讨论事务与跨度(transactions vs. spans
)。
跟踪和跨度标识符
到目前为止,我们已经确定了跟踪的组件,但我们还没有描述这些组件是如何链接在一起的。
首先,每个跟踪都用跟踪标识符(trace identifier
)唯一标识。 这是通过在根跨度(root span
)中创建一个唯一的随机生成值(即 UUID
)来完成的——这是启动整个跟踪的初始操作。 在我们上面的示例中,根跨度出现在浏览器应用程序中。
其次,每个 span
首先需要被唯一标识。 这通过在跨度开始其操作时创建唯一的跨度标识符(或 span_id
)来完成。这个 span_id
创建应该发生在 trace
内发生的每个 span
(或操作)处进行。
让我们重新审视我们假设的跟踪示例。 在上图中,您会注意到跟踪标识符唯一地标识了跟踪,并且该跟踪中的每个跨度也拥有一个唯一的跨度标识符。
然而,生成 trace_id
和 span_id
是不够的。 要实际连接这些服务,您的应用程序必须在从一个服务向另一个服务发出请求时传播所谓的跟踪上下文(trace context
)。
跟踪上下文
跟踪上下文(trace context
)通常仅由两个值组成:
- 跟踪标识符(或
trace_id
):在根跨度中生成的唯一标识符,用于标识整个跟踪。 这与我们在上一节中介绍的跟踪标识符相同;它以不变的方式传播到每个下游服务。 - 父标识符(或
parent_id
):产生当前操作的“父”跨度的span_id
。
下图显示了在一个服务中启动的请求如何将跟踪上下文传播到下游的下一个服务。 您会注意到 trace_id
保持不变,而 parent_id
在请求之间发生变化,指向启动最新操作的父跨度。
有了这两个值,对于任何给定的操作,就可以确定原始(root
)服务,并按照导致当前操作的顺序重建所有父/祖先(parent/ancestor
)服务。
工作示例(代码演示)
示例源码:
为了更好地理解这一点,让我们实际实现一个基本的跟踪实现,其中浏览器应用程序是由跟踪上下文连接的一系列分布式操作的发起者。
首先,浏览器应用程序呈现一个表单:就本示例而言,是一个“邀请用户(invite user)”
表单。表单有一个提交事件处理程序,它在表单提交时触发。 让我们将此提交处理程序视为我们的根跨度(root span
),这意味着当调用处理程序时,会生成 trace_id
和 span_id
。
接下来,完成一些工作以从表单中收集用户输入的值,然后最后向我们的 Web 服务器发出一个到 /inviteUser
API 端点的 fetch
请求。作为此 fetch
请求的一部分,跟踪上下文作为两个自定义 HTTP header
传递:trace-id
和 parent-id
(即当前 span
的 span_id
)。
// browser app (JavaScript)
import uuid from 'uuid';
const traceId = uuid.v4();
const spanId = uuid.v4();
console.log('Initiate inviteUser POST request', `traceId: ${traceId}`);
fetch('/api/v1/inviteUser?email=' + encodeURIComponent(email), {
method: 'POST',
headers: {
'trace-id': traceId,
'parent-id': spanId,
}
}).then((data) => {
console.log('Success!');
}).catch((err) => {
console.log('Something bad happened', `traceId: ${traceId}`);
});
请注意,这些是用于说明目的的非标准 HTTP header
。 作为 W3C traceparent
规范的一部分,正在积极努力标准化 tracing HTTP header
,该规范仍处于 “Recommendation”
阶段。
在接收端,API web server
处理请求并从 HTTP
请求中提取跟踪元数据(tracing metadata
)。然后它会排队一个 job
以向用户发送电子邮件,并将跟踪上下文作为 job
描述中“meta”字段的一部分附加。最后,它返回一个带有 200
状态 code
的响应,表明该方法成功。
请注意,虽然服务器返回了成功的响应,但实际的“工作”直到后台任务 worker
拿起新排队的 job
并实际发送电子邮件后才完成。
在某个点上,队列处理器开始处理排队的电子邮件作业。再一次,跟踪(trace
)和父标识符(parent identifier
)被提取出来,就像它们在 web server
中的早些时候一样。
// API Web Server
const Queue = require('bull');
const emailQueue = new Queue('email');
const uuid = require('uuid');
app.post("/api/v1/inviteUser", (req, res) => {
const spanId = uuid.v4(),
traceId = req.headers["trace-id"],
parentId = req.headers["parent-id"];
console.log(
"Adding job to email queue",
`[traceId: ${traceId},`,
`parentId: ${parentId},`,
`spanId: ${spanId}]`
);
emailQueue.add({
title: "Welcome to our product",
to: req.params.email,
meta: {
traceId: traceId,
// the downstream span's parent_id is this span's span_id
parentId: spanId,
},
});
res.status(200).send("ok");
});
// Background Task Worker
emailQueue.process((job, done) => {
const spanId = uuid.v4();
const { traceId, parentId } = job.data.meta;
console.log(
"Sending email",
`[traceId: ${traceId},`,
`parentId: ${parentId},`,
`spanId: ${spanId}]`
);
// actually send the email
// ...
done();
});
分布式系统 Logging
您会注意到,在我们示例的每个阶段,都会使用 console.log
进行 logging
调用,该调用还发出当前 trace
、span
和 parent
标识符。 在完美的同步世界中——每个服务都可以登录到同一个集中式 logging
工具——这些日志语句中的每一个都会依次出现:
如果在这些操作过程中发生异常或错误行为,使用这些或额外的日志语句来查明来源将相对简单。但不幸的现实是,这些都是分布式服务,这意味着:
- Web 服务器通常处理许多并发请求。Web 服务器可能正在执行归因于其他请求的工作(并发出日志记录语句)。
- 网络延迟会影响操作顺序。 从上游服务发出的请求可能不会按照它们被触发的顺序到达目的地。
- 后台
worker
可能有排队的job
。在到达此跟踪中排队的确切job
之前,worker
可能必须先完成先前排队的job
。
在一个更现实的例子中,我们的日志调用可能看起来像这样,它反映了同时发生的多个操作:
如果不跟踪 metadata
,就不可能了解哪个动作调用哪个动作的拓扑结构。 但是通过在每次 logging
调用时发出跟踪 meta
信息,可以通过过滤 traceId
快速过滤跟踪中的所有 logging
调用,并通过检查 spanId
和 parentId
关系重建确切的顺序。
这就是分布式跟踪的威力:通过附加描述当前操作(span id
)、产生它的父操作(parent id
)和跟踪标识符(trace id
)的元数据,我们可以增加日志记录和遥测数据以更好地理解 分布式服务中发生的事件的确切顺序。
在真实的分布式跟踪环境中
在本文的过程中,我们一直在使用一个有点人为的示例。 在真正的分布式跟踪环境中,您不会手动生成和传递所有的跨度和跟踪标识符。 您也不会依赖 console.log
(或其他日志记录)调用来自己发出跟踪元数据。 您将使用适当的跟踪库来为您处理检测和发送跟踪数据。
OpenTelemetry
OpenTelemetry
是一组开源工具、API
和 SDK
,用于检测、生成和导出正在运行的软件中的遥测数据。 它为大多数流行的编程语言提供了特定于语言的实现,包括浏览器 JavaScript
和 Node.js
。
Sentry
Sentry
以多种方式使用这种遥测。例如,Sentry
的性能监控功能集使用跟踪数据生成瀑布图,说明跟踪中分布式服务操作的端到端延迟。
Sentry
还使用跟踪元数据来增强它的错误监控功能,以了解在一个服务(如服务器后端)中触发的错误如何传播到另一个服务(如前端)中的错误。
Sentry 监控 - 全栈开发人员的分布式跟踪 101 系列教程(第一部分)的更多相关文章
- 诚聘:全栈开发人员,三线城市10-16K
北京快鸽联盟信息技术有限公司成立于2013年,专注于校园及社区快递和增值服务.目前已有十余家各地分部,并与上百所大学,各大快递和电商公司有密切合作,年处理快件量超千万,长期处于行业领先地位. 诚聘全栈 ...
- python全栈开发day24-__new__、__del__、item系列、异常处理
一.昨日内容回顾 1.反射 用字符串类型的名字,操作命名空间的变量. 反射使用场景:明显的简化代码,能拿到的变量名本来就是一个字符串类型的时候, 用户输入的,文件读入的,网上传输的 2.__call_ ...
- [勘探开发]成绩,全栈开发,健全&借贷
开发探索的一些update: 将结果做为开发的基础和终极目标 开发人员从过程的追求到最后结果的追求是一个质变的过程.相当于NBA中得分王和总冠军的差别: 一个是完毕一个局部的本职工作(有时候会和项目的 ...
- Python 全栈开发【第0篇】:目录
Python 全栈开发[第0篇]:目录 第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...
- 全栈开发必备的10款Sublime Text 插件
Sublime Text 具有漂亮的用户界面和强大的功能,例如代码缩略图,多重选择,快捷命令等.Sublime Text 更妙的是它的可扩展性.所以,这里挑选了全栈开发必备的10款 Sublime T ...
- 全栈开发必备的10款 Sublime Text 插件
Sublime Text 具有漂亮的用户界面和强大的功能,例如代码缩略图,多重选择,快捷命令等.Sublime Text 更妙的是它的可扩展性.所以,这里挑选了全栈开发必备的10款 Sublime T ...
- 处女作《Web全栈开发进阶之路》出版了!
书中源码下载地址:https://github.com/qinggee/WebAdvanced 01. 当初决定写博客的原因非常的纯洁:只要每个月写上 4 篇以上博客,月底的绩效奖金就多 500 块. ...
- 转-subl配置全栈开发环境
为 Sublime Text 3 设置 Python 的全栈开发环境 Sublime Text 3 (ST3) 是一个轻量级的跨平台文字编辑器,尤以其轻快的速度,易用性和强大的社区支持而著称.它一经面 ...
- CabloyJS带你轻松走进NodeJS全栈开发-免费课程 作者亲授
课程说明 B站直播 为回馈新老同学对开源框架CabloyJS的支持与厚爱,快速而轻松的开启NodeJS全栈开发之旅.2019年9月5日至9月11日在B站开启了一波免费直播培训课程 课程信息,请点击链接 ...
随机推荐
- Swagger2常用注解解析(轻松构建Swagger)
Swagger2常用注解解析 一.SpringBoot集成Swagger2 二.常用注解解析 具体使用举例说明: 一.SpringBoot集成Swagger2 引入相关jar包 <!-- swa ...
- BeanUtils中的自动类型转换(二)
javabean package entity; import java.util.Date; /** * 一个测试用: * student,javaBean * @author mzy * 一个标准 ...
- Go: 复合数据类型struct
结构体 结构体是将零个或多个任意类型的命名变量组合在一起的聚合数据类型.每个变量都叫做结构体的成员. type Employee struct { ID int Name string age int ...
- 技术调研,IDEA 插件怎么开发「脚手架、低代码可视化编排、接口生成测试」?
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 不踩些坑,根本不是成熟的码农! 你觉得肯德基全家桶是什么?一家人一起吃的桶吗,就那么 ...
- 从IT圈“鄙视链”看前端开发有多难?
如今"鄙视链"体现在生活的方方面面,各行各业都有默认一致的鄙视链.IT圈子因为开发语言多样.工程师岗位种类多.技术框架多,也有自己圈子内的鄙视链.按照开发工程师的岗位形成的鄙视链是 ...
- set类型数据的操作指令
集合无序,无下标. 1. 也可以在集合上继续添加元素. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.
- Defence
emm...这道题我调了一下午你敢信?? 好吧还是我太天真了. 开始的时候以为自己线段树动态开点与合并写错了,就调; 结果发现没问题,那就是信息维护错了. 一开始以为自己最左右的1 ...
- 眼镜选款新方法,用AR+Scene技术实现3D虚拟试戴
互联网和智慧终端的普及促进了电商的产生和蓬勃发展,而新技术的产生,则推动着电商领域的不断升级.疫情使得人们更加习惯于使用电商进行购物,但对传统的线上购物模式已经产生了一些厌倦,电商市场急需模式上的变革 ...
- Git 系列教程(10)- 仓库别名
Git 别名 前言 Git 并不会在你输入部分命令时自动推断出你想要的命令 如果不想每次都输入完整的 Git 命令,可以通过 git config 文件来轻松地为每一个命令设置一个别名 $ git c ...
- ubantu虚拟机搭建xl2tp服务
在编译成功源码,安装完毕xl2tpd后,便可以配置xl2tpd服务.基本配置过程主要涉及两个配置文件:一个用来配置xl2tpd, 一个用来配置ppp协议.下面分别对这两个文件进行说明,最后添加xl2t ...