微服务架构具有诸多优势,包括增强团队自主性、提高扩展与部署灵活性。但缺点是,系统中的服务越多(一个微服务应用可能包含几十个甚至几百个服务),清晰地了解系统总体运行情况的难度就越大。作为复杂软件系统的编写者和维护者,我们深知掌握系统运行情况的重要性。可观测性工具可帮助我们清晰地了解众多服务和支持基础设施。
在本教程中,我们将重点介绍微服务应用的一种非常重要的可观测性工具:链路追踪(tracing)。在步入正题之前,让我们先定义一下讨论可观测性时通常会用到的一些术语:
我们可借助所有这些概念来了解微服务的性能。链路追踪是可观测性策略中特别有用的部分,因为链路追踪提供了发出请求时多个通常松散耦合的组件之间的“全局视图”。这也是识别性能瓶颈的一种尤为高效的方法。
本教程使用了来自 OpenTelemetry (OTel) 的链路追踪工具套件,OTel 是一套用于收集、处理并导出遥测数据的厂商中立的开源标准,正快速被广泛采用。在 OTel 的概念中,链路追踪将一个可能包括多个服务的数据流分成了一系列按时间顺序排列的数据块,以便于您理解:
本教程主要介绍了如何利用 OTel 来跟踪微服务应用的操作。在本教程的四个挑战中,您将学习如何跟踪通过系统的请求并解决有关自身微服务的问题:
以下挑战介绍了在首次设置链路追踪时我们推荐使用的流程。具体步骤:
注:我们旨在通过本教程阐释一些有关遥测的核心概念,而非展示如何在生产环境中正确部署微服务。虽然我们会用到真正的“微服务”架构,但需要做以下几点说明:
下图显示了本教程中所用的微服务及其他元素之间的整体架构和数据流。
两种微服务如下:
https://github.com/microservices-march/messenger
) - 一个简单的聊天 API,具有消息存储功能https://github.com/microservices-march/notifier
) - 一个根据用户偏好触发事件以提醒用户的监听器三种支持基础架构包括:
现在暂时把 OTel 搁置脑后,重点介绍下我们正在跟踪的事件序列,即当用户发送新的聊天消息并且接收者收到相关通知时会发生什么。
流程分解如下:
在设置遥测工具时,最好先确定一组明确的监测目标,而不是“发送所有内容,希望获得洞察”。在本教程中,我们有三个主要的遥测目标:
请注意,这些目标与系统的技术操作和用户体验有关。
若在自己的环境中完成本教程的学习,您需要:
bash
(本教程会提供并解释所有代码和命令,因此即使您知识有限也无妨)asdf
,获取与教程中所用完全相同的 Node.js 版本。curl
(已安装在大多数系统上)注:本教程用到了 JavaScript SDK,因为信使和通知器服务均使用 Node.js 编写而成。您还需要设置 OTel 自动埋点特性,以便了解 OTel 提供的信息类型。本教程介绍了有关 OTel Node.js SDK 的常用信息,如欲了解更多详情,请参阅 [OTel 文档]:https://opentelemetry.io/docs/instrumentation/js/getting-started/nodejs/
。
注:本教程中省略了 Linux 命令行提示符,以便您将命令复制和粘贴到终端。波浪符 (
**~**
) 表示您的主目录。
mkdir ~/microservices-march
cd ~/microservices-march
git clone https://github.com/microservices-march/messenger --branch mm23-metrics-start
git clone https://github.com/microservices-march/notifier --branch mm23-metrics-start
git clone https://github.com/microservices-march/platform --branch mm23-metrics-start
在这个挑战中,启动信使服务并配置 OTel 自动埋点以将遥测数据发送至控制台。
cd ~/microservices-march/platform
docker compose up -d --build
这将同时启动 RabbitMQ 和 Jaeger——两者将在后面的挑战中用到。
‑d
标记指示 Docker Compose 在容器启动时与之分离(否则容器将始终与您的终端保持连接)。--build
标记指示 Docker Compose 在启动时重建所有镜像。这可确保您正在运行的镜像通过任何潜在的文件变更保持更新。cd ~/microservices-march/messenger/app
asdf install
npm install
docker compose up -d
npm run refresh-db
借助 OTel 自动埋点,无需修改信使代码库中的任何内容即可设置链路追踪。所有链路追踪配置并非直接编写在应用代码中,而是在脚本中定义,然后在运行时把脚本导入 Node.js 进程。
此处,您可以配置信使服务的自动埋点使用最基本的链路追踪目标位置,即控制台。在挑战 2 中,您需要更改配置,将链路追踪发送到作为外部收集器的 Jaeger。
npm install @opentelemetry/sdk-node@0.36.0 \
@opentelemetry/auto-instrumentations-node@0.36.4
下列库提供了以下功能:
@opentelemetry/sdk-node
——生成并导出 OTel 数据@opentelemetry/auto-instrumentations-node
——使用所有常见 Node.js 埋点的默认配置进行自动设置注:OTel 的不同之处在于,其 JavaScript SDK 被分解成了多个微小部分。因此,在本教程的基本示例中,您将需要多安装几个安装包。如欲了解除本教程中所列以外还需要哪些安装包才能完成监测任务,请仔细阅读(强推)OTel [入门指南]:
https://opentelemetry.io/docs/instrumentation/js/getting-started
并查看 OTel [GitHub 代码库]:https://github.com/open-telemetry/opentelemetry-js#packages
。
touch tracing.mjs
//1
import opentelemetry from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
//2
const sdk = new opentelemetry.NodeSDK({
traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
instrumentations: [getNodeAutoInstrumentations()],
});
//3
sdk.start();
这些代码会完成以下操作:
@opentelemetry/instrumentation-pg
库 (pg
)@opentelemetry/instrumentation-express
@opentelemetry/instrumentation-amqplib
库 (amqplib
)ConsoleSpanExporter
)。node --import ./tracing.mjs index.mjs
稍后,控制台(您的终端)中开始显示大量与链路追踪相关的输出:
...
{
traceId: '9c1801593a9d3b773e5cbd314a8ea89c',
parentId: undefined,
traceState: undefined,
name: 'fs statSync',
id: '2ddf082c1d609fbe',
kind: 0,
timestamp: 1676076410782000,
duration: 3,
attributes: {},
status: { code: 0 },
events: [],
links: []
}
...
注:让终端会话保持打开状态,以便在挑战 2 中使用。
您可以使用许多工具来查看和分析链路追踪,但本教程使用的是 Jaeger。Jaeger 是一个简单的开源端到端分布式链路追踪框架,内置一个基于 Web 的用户界面,用于查看 span 及其他链路追踪数据。平台代码库中提供的基础架构包括 Jaeger(已在挑战 1 的第一步中启动),因此您可以专注于分析数据,不用考虑工具问题。
您可通过在浏览器中访问 http://localhost:16686 端点来访问 Jaeger,但如果您现在就访问该端点,不会看到与您的系统有关的任何内容。这是因为您目前收集的链路追踪正被发送到控制台!如欲在 Jaeger 中查看链路追踪数据,需使用 OpenTelemetry 协议 (OTLP) 格式导出链路追踪。
在这个挑战中,您需要为以下服务配置埋点以监测核心用户流:
值得一提的是,使用 OTel 自动埋点意味着您无需修改信使代码库中的任何内容即可设置链路追踪。但所有链路追踪配置都位于在运行时被导入 Node.js 进程的脚本中。此处,您可将由信使服务生成的链路追踪的目标位置从控制台更改为外部收集器(在本教程中为 Jaeger)。
npm install @opentelemetry/exporter-trace-otlp-http@0.36.0
@opentelemetry/exporter-trace-otlp-http
库通过 HTTP 导出 OTLP 格式的链路追踪信息,用于向 OTel 外部收集器发送遥测数据。
打开 tracing.mjs(已在挑战 1 中创建和编辑),并进行以下修改:
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
注:为了简单起见,本教程假设收集器位于默认位置 http://localhost:4318/v1/traces。在真实系统中,最好明确设置该位置。
traceExporter:new opentelemetry.tracing.ConsoleSpanExporter(),
替换为:
traceExporter: new OTLPTraceExporter({ headers: {} }),
import
语句中:按下 Ctrl+c
停止信使服务,该终端是在配置 OTel 自动埋点发送到控制台的第四步中启动的。重启该服务,以使用在第二步中配置的新导出器:
^c
node --import ./tracing.mjs index.mjs
curl -X GET http://localhost:4000/health
在发送请求前等待 10 秒钟有助于您的跟踪更容易被找到,因为在服务启动时自动埋点会生成许多链路追踪。
jsonParser
部分所用的时长。
Ctrl+c
来停止信使服务。 然后再安装几个 Node.js 包:^c
npm install @opentelemetry/semantic-conventions@1.10.0 \
@opentelemetry/resources@1.10.0
下列两个库提供以下功能:
@opentelemetry/semantic-conventions
——定义了 OTel 规范中所定义的链路追踪的标准属性。@opentelemetry/resources
——定义一个对象(资源)以代表生成 OTel 数据的来源(在本教程中为信使服务)。在文本编辑器中打开 tracing.mjs 并进行以下修改:
import
语句中:
import { Resource } from "@opentelemetry/resources";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
messenger
的 resource
,具体方法是在最后一个 import
语句后添加以下行:
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "messenger",
});
resource
对象传递给 NodeSDK 构造函数:
const sdk = new opentelemetry.NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ headers: {} }),
instrumentations: [getNodeAutoInstrumentations()],
});
重启信使服务:
node --import ./tracing.mjs index.mjs
curl -X GET http://localhost:4000/health
注:让客户端终端保持打开状态,以便在下一部分中再次使用,同时让信使终端保持打开状态,以在挑战 3 中再次使用。
现在为通知器服务启动并配置自动埋点,运行与前两部分中信使服务基本相同的命令。
cd ~/microservices-march/notifier/app
asdf install
npm install
docker compose up -d
npm run refresh-db
npm install @opentelemetry/auto-instrumentations-node@0.36.4 \
@opentelemetry/exporter-trace-otlp-http@0.36.0 \
@opentelemetry/resources@1.10.0 \
@opentelemetry/sdk-node@0.36.0 \
@opentelemetry/semantic-conventions@1.10.0
touch tracing.mjs
import opentelemetry from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { Resource } from "@opentelemetry/resources";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "notifier",
});
const sdk = new opentelemetry.NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ headers: {} }),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
注:本脚本与信使(notifier)服务所用的完全相同,唯一不同之处是SemanticResourceAttributes.SERVICE_NAME
字段中的值是 notifier
。
node --import ./tracing.mjs index.mjs
curl http://localhost:5000/health
注:让客户端终端和通知器终端保持打开状态,以便在挑战 3 中再次使用。
对于 NGINX,您需要手动设置链路追踪,而不是使用 OTel 自动埋点方法。目前,使用 OTel 对 NGINX 进行监测的最常见方式是使用[用 C 语言编写的模块]:https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module#nginx-webserver-module
。第三方模块是 NGINX 生态系统的一个重要组成部分,但需要进行一些设置。本教程向您展示了如何进行这些设置。有关背景信息,请参阅我们的博文《为 NGINX 和 NGINX Plus 编译第三方动态模块》。
cd ~/microservices-march/messenger/
mkdir load-balancer
cd load-balancer
touch Dockerfile
touch nginx.conf
touch opentelemetry_module.conf
FROM --platform=amd64 nginx:1.23.1
# 用我们自己的文件替换 nginx.conf 文件
COPY nginx.conf /etc/nginx/nginx.conf
# 定义 NGINX OTel 模块的版本
ARG OPENTELEMETRY_CPP_VERSION=1.0.3
# 定义编译和运行 NGINX 时所用共享库的搜索路径
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/opentelemetry-webserver-sdk/sdk_lib/lib
# 1. 下载最新版本的 Consul 模板和 OTEL C++ Web 服务器模块、otel-webserver-module
ADD https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv${OPENTELEMETRY_CPP_VERSION}/opentelemetry-webserver-sdk-x64-linux.tgz /tmp
RUN apt-get update \
&& apt-get install -y --no-install-recommends dumb-init unzip \
# 2. 提取模块文件
&& tar xvfz /tmp/opentelemetry-webserver-sdk-x64-linux.tgz -C /opt \
&& rm -rf /tmp/opentelemetry-webserver-sdk-x64-linux.tgz \
# 3. 将‘load_module’指令安装并添加至主 NGINX 配置文件的顶部
&& /opt/opentelemetry-webserver-sdk/install.sh \
&& echo "load_module /opt/opentelemetry-webserver-sdk/WebServerModule/Nginx/1.23.1/ngx_http_opentelemetry_module.so;\n$(cat /etc/nginx/nginx.conf)" > /etc/nginx/nginx.conf
# 4. 复制到 NGINX OTel 模块的配置文件中
COPY opentelemetry_module.conf /etc/nginx/conf.d/opentelemetry_module.conf
EXPOSE 8085
STOPSIGNAL SIGQUIT
events {}
http {
include /etc/nginx/conf.d/opentelemetry_module.conf;
upstream messenger {
server localhost:4000;
}
server {
listen 8085;
location / {
proxy_pass http://messenger;
}
}
}
这个基本的 NGINX 配置文件指示 NGINX:
注:这与 NGINX 在生产环境中作为反向代理和负载均衡器的实际配置很相似。唯一的主要区别是,upstream
块中 server
指令的参数通常是域名或 IP 地址,而非 localhost
。
NginxModuleEnabled ON;
NginxModuleOtelSpanExporter otlp;
NginxModuleOtelExporterEndpoint localhost:4317;
NginxModuleServiceName messenger-lb;
NginxModuleServiceNamespace MicroservicesMarchDemoArchitecture;
NginxModuleServiceInstanceId DemoInstanceId;
NginxModuleResolveBackends ON;
NginxModuleTraceAsError ON;
docker build -t messenger-lb .
docker run --rm --name messenger-lb -p 8085:8085 --network="host" messenger-lb
curl http://localhost:8085/health
注:让 NGINX 和客户端终端保持打开状态,以便在挑战 3 中再次使用。
在架构和用户流中,我们概述了用户流的各个阶段,现在简单回顾一下:
实施遥测的目标是:
在这个挑战中,您将学习如何评估 OTel 埋点生成的链路追踪是否满足上述目标要求。首先,运行系统并创建一些链路追踪数据。然后,检查消息流的链路追踪以及其中分别由 NGINX、信使服务和通知器服务生成的部分。
在客户端终端,设置对话并在两位用户之间发送几条消息:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"participant_ids": [1, 2]}' \
'http://localhost:8085/conversations'
curl -X POST \
-H "User-Id: 1" \
-H "Content-Type: application/json" \
-d '{"content": "This is the first message"}' \
'http://localhost:8085/conversations/1/messages'
curl -X POST \
-H "User-Id: 2" \
-H "Content-Type: application/json" \
-d '{"content": "This is the second message"}' \
'http://localhost:8085/conversations/1/messages'
通知器服务生成如下输出,并显示在通知器终端中:
Received new_message: {"type":"new_message","channel_id":1,"user_id":1,"index":1,"participant_ids":[1,2]}
Sending notification of new message via sms to 12027621401
Received new_message: {"type":"new_message","channel_id":1,"user_id":2,"index":2,"participant_ids":[1,2]}
Sending notification of new message via email to the_hotstepper@kamo.ze
Sending notification of new message via sms to 19147379938
在浏览器中打开 Jaeger 用户界面,从 Service 下拉菜单中选择 messenger-lb,然后点击 Find Traces 按钮。 此时界面上会显示一个链路追踪列表,回溯到用户流起初。点击任何链路追踪,即可显示相关详情,如下截图所示:
点击并查看详情。在继续操作之前,考虑一下链路追踪中的信息可如何帮助您实现挑战 3 的简介中提到的监测目标。相关问题包括:
从 NGINX span 开始,父 span 包含 11 个子 span。由于当前的 NGINX 配置非常简单,因此子 span 意义不大,只显示了 NGINX 请求处理生命周期中每个步骤所用的时间。但 父 span(第一个)提供了一些有价值的信息:
/conversations/1/messages
发送了一个 POST
请求,响应为 201
(创建成功)”。这与架构和用户流中的第一步和第 4a 步相对应)。http.method
字段——POST
(在 REST 术语中,这意味着创建)http.status_code
字段——201
(表示创建成功)http.target
字段——conversations/1/messages
(消息端点)webengine.name
字段显示这是该请求的 NGINX 部分。此外,由于信使和通知器的 span 嵌套在 messenger-lb conversations/1
span 内(如准备好解读链路追踪中的截图所示),您可以判断通过 NGINX 反向代理发送给信使服务的请求是否到达了流程中的所有预期组件。
该信息满足目标要求,因为您可以看到 NGINX 反向代理是流程的一部分。
在标记 messenger-lb 的 span 列表中,查看最新 span(位于列表的底部),以了解请求的 NGINX 部分所用时长。在截图中,span 从 589 微秒 (µs) 开始,持续了 24µs,这意味着整个反向代理操作只花了 613µs——约 0.6 毫秒 (ms)。(当然,当您自己操作本教程步骤时,具体数值会有所不同)。
在这样的设置中,大多数情况下,这些值只是相对于其他度量值而言有用,并随系统而变。不过在本例中,该操作的时长显然不到 5 秒阈值。
该信息满足目标要求,因为您可以看到 NGINX 操作的时长都不曾接近 5 秒。如果消息流中出现了非常缓慢的操作,那一定是后来发生的。
NGINX 反向代理层并不包括任何相关信息,因此您可以转到信使 span。
链路追踪的信使服务部分包含另外 11 个 span。同样,大多数子 span 涉及 Express 框架在处理请求时使用的基本步骤,意义不大。但父 span(第一个)再次提供了一些有价值的信息:
在 Tags 下,您能够看到以下属性:
http.method
字段——POST
(同样在 REST 术语中,这意味着创建)http.route
字段——/conversations/:conversationId/messages
(消息路由)http.target
字段——/conversations/1/messages
(消息端点)该信息满足目标要求,因为从中可以看出信使服务是流程的一部分,而且到达的端点是新的消息端点。
如下截图所示,链路追踪的信使部分开始于 1.28 ms,结束于 36.28ms,总时长为 35ms。其中大部分时间花在解析 JSON(middleware
-
jsonParser
)和连接数据库(pg-pool.connect
和 tcp.connect
)上。
鉴于在消息编写过程中还进行了几次 SQL 查询,因此这也很合理。这反过来表明,您可能需要增加自动埋点配置,以捕获这些查询的用时。(本教程没有用到这个额外的埋点,因此在挑战 4 中,您会手动创建 span,后者可用于打包数据库查询。)
该信息满足目标要求,因为从中可以看出信使操作的时长都不曾接近 5 秒。如果消息流中出现了非常缓慢的操作,那一定是后来发生的。
与 NGINX span 一样,信使 span 不包含这些信息,因此您可以转到通知器 span。
链路追踪的通知器部分只包含两个 span:
chat_queue
process
span——确认通知器服务处理了来自 chat_queue 消息队列的事件pg-pool.connect
span——显示处理完事件后,通知器服务与其数据库建立了某种连接仅凭从这些 span 中获取的信息,无法全面了解每一步。您可以看到通知器服务使用来自队列的事件,但却不知道:
这表明您需要执行以下操作才能充分了解通知器服务流:
通过查看通知器服务 span 的总用时,您可以看到请求在消息流的通知器部分花费了 30.77 ms。但是,由于没有表示整个消息流(向接收者发送通知)“结束”的 span,因此您无法确定消息流这一部分的总用时或操作完成的总用时。
您可以看到通知器服务的 chat_queue
process
开始于 6.12ms,即信使服务的 chat_queue
send
开始(于 4.12ms)后 2ms。
该目标达成了,因为您知道通知器在信使 服务派发事件 2ms 后使用了该事件。与目标 2 不同的是,您不需要知道事件是否已被完全处理,也不需要知道耗时多少便可实现此目标。
根据我们对当前 OTel 自动埋点生成的链路追踪的分析,可以清楚地发现:
request
handler
以及所有数据库操作的 span。一些中间件 span(例如 expressInit
和 corsMiddleware
)似乎不相关,可以移除。这意味着,基本埋点满足了最后一个目标要求:
然而,没有足够的信息来实现前两个目标:
在这个挑战中,您将需要根据在挑战 3 中完成的追踪分析,优化 OTel 埋点。其中包括删除不必要的 span, 创建新的自定义 span,并确认通知器服务使用的事件是信使服务生成的事件。
const IGNORED_EXPRESS_SPANS = new Set([
"middleware - expressInit",
"middleware - corsMiddleware",
]);
这定义了一组 span 名称,这些名称来自于下面 Jaeger 用户界面截图中的 span 列表,但由于无法为该消息流提供有用的信息而将从追踪中删除。您可能决定也不需要该截图中所列的其他 span,并将其添加到 IGNORED_EXPRESS_SPANS
列表中。
const sdk = new opentelemetry.NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ headers: {} }),
instrumentations: [getNodeAutoInstrumentations()],
});
更改为以下行:
const sdk = new opentelemetry.NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ headers: {} }),
instrumentations: [
getNodeAutoInstrumentations({
"@opentelemetry/instrumentation-express": {
ignoreLayers: [
(name) => {
return IGNORED_EXPRESS_SPANS.has(name);
},
],
},
}),
],
});
getNodeAutoInstrumentations
函数引用了第一步中定义的 span 集,以将这些 span 从 @opentelemetry/instrumentation-express
生成的链路追踪中滤除。换而言之,对于属于 IGNORED_EXPRESS_SPANS
的 span,将 return
语句解析为 true
,同时 ignoreLayers
语句从链路追踪中移除该 span。
Ctrl+c
来停止信使服务。然后重启它。^c
node --import ./tracing.mjs index.mjs
curl -X POST \
-H "User-Id: 2" \
-H "Content-Type: application/json" \
-d '{"content": "This is the second message"}' \
'http://localhost:8085/conversations/1/messages'
middleware
span — expressInit
和 corsMiddleware
不再显示(您可以将其与挑战 3 中检查链路追踪的信使部分的目标 2 截图进行比较)。
在这一部分中,您将首次接触到应用代码。自动埋点无需更改应用便可生成大量信息,但有些见解必须通过对业务逻辑的特定部分进行监测才能获取。
对于您正在监测的新消息流,一个示例是跟踪向消息接收者发送通知。
import
语句列表的末尾添加以下行:import { trace } from "@opentelemetry/api";
for (let pref of preferences) {
console.log(
`Sending notification of new message via ${pref.address_type} to ${pref.address}`
);
}
用以下代码替换上文:
const tracer = trace.getTracer("notifier"); // 1
tracer.startActiveSpan( // 2
"notification.send_all",
{
attributes: {
user_id: msg.user_id,
},
},
(parentSpan) => {
for (let pref of preferences) {
tracer.startActiveSpan( // 3
"notification.send",
{
attributes: { // 4
notification_type: pref.address_type,
user_id: pref.user_id,
},
},
(span) => {
console.log(
`Sending notification of new message via ${pref.address_type} to ${pref.address}`
);
span.end(); // 5
}
);
}
parentSpan.end(); // 6
}
);
新代码会执行以下操作:
tracer
,它是一个全局对象,用于与 OTel 链路追踪进行交互。notification.send_all
的新的父 span,并设置 user_id
属性以识别消息的发送者。notification.send_all
下新建了一个名为 notification.send
的子 span。每个通知都会生成一个新 span。notification_type
——短信或电子邮件之一user_id
——通知接收用户的 IDnotification.send
。notification.send_all
。拥有父 span 可确保每个“发送通知”操作都会被报告,即使没有发现用户的通知偏好。
Ctrl+c
来停止通知器服务。然后重启它:^c
node --import ./tracing.mjs index.mjs
curl -X POST \
-H "User-Id: 2" \
-H "Content-Type: application/json" \
-d '{"content": "This is the second message"}' \
'http://localhost:8085/conversations/1/messages'
现在您可以完全实现第一个和第二个目标,因为您可以看到一个请求在新的消息流中所经历的所有步骤。在每个 span 上所花费的时间能够暴露这些步骤之间的任何延迟。
如欲全面了解消息流,您还需要确认以下一点。通知器服务处理的事件实际上是信使服务派发的事件吗?
您不必做出任何明确的修改便可连接这两个追踪,但您也不能盲目相信自动埋点。
考虑到这一点,您可以添加一些快速调试代码,以验证在 NGINX 服务中启动的追踪确实与通知器服务所用的追踪相同(具有相同的追踪 ID)。
打开信使代码库的 app 目录下的 index.mjs 文件,并进行以下修改:
import { trace } from "@opentelemetry/api";
async function createMessageInConversation(req, res) {
const tracer = trace.getActiveSpan();
console.log("TRACE_ID: ", tracer.spanContext().traceId);
新行列出了信使中处理新消息创建的函数内的 TRACE_ID
。打开通知器代码库的 app 目录下的 index.mjs 文件,并在现有的黑色行下面添加橙色高亮显示的行:
export async function handleMessageConsume(channel, msg, handlers) {
console.log("RABBIT_MQ_MESSAGE: ", msg);
新行列出了通知服务接收的 AMQP 事件的全部内容。
^c
node --import ./tracing.mjs index.mjs
curl -X POST \
-H "User-Id: 2" \
-H "Content-Type: application/json" \
-d '{"content": "This is the second message"}' \
'http://localhost:8085/conversations/1/messages'
TRACE_ID: 29377a9b546c50be629c8e64409bbfb5
_spanContext: {
traceId: '29377a9b546c50be629c8e64409bbfb5',
spanId: 'a94e9462a39e6dbf',
traceFlags: 1,
traceState: undefined
},
注:在实际生产系统中,一旦确认流程按预期运行,即可删除您在本部分中添加的代码。
整个教程下来,您创建了不少容器和镜像!使用以下指令来将其删除。
docker rm $(docker stop messenger-lb)
cd ~/microservices-march/platform && docker compose down
cd ~/microservices-march/notifier && docker compose down
cd ~/microservices-march/messenger && docker compose down
恭喜,您已学完本教程!
然而,您接触的仅仅是链路追踪配置的基础知识!在生产环境中,您可能希望为每个数据库查询添加自定义 span 等内容,并在所有 span 上添加额外的元数据来描述运行时细节(例如每个服务的容器 ID)。您还可以实现另外两种 OTel 数据类型(指标和日志),以全面了解系统的运行状况。
原文链接:https://www.nginx.com/blog/nginx-tutorial-opentelemetry-tracing-understand-microservices/