这篇文章是帮助您将2023 年 3 月微服务中的概念付诸实践的四个教程之一: 开始交付微服务:
微服务架构具有许多好处,包括增强团队自主性以及提高扩展和部署的灵活性。 不利的一面是,系统中的服务越多(微服务应用程序可能有几十个甚至几百个服务),就越难以清晰地了解系统的整体运行情况。 作为复杂软件系统的编写者和维护者,我们知道拥有清晰的图景至关重要。 可观察性工具使我们能够在众多服务和支持基础设施中构建该图像。
在本教程中,我们重点介绍微服务应用程序的一种非常重要的可观察性类型:跟踪。 在开始之前,让我们定义讨论可观察性时常用的一些术语:
我们可以使用所有这些概念来深入了解微服务的性能。 跟踪是可观察性策略中特别有用的部分,因为跟踪提供了在发出请求时多个通常是松散耦合的组件中发生的情况的“全景”。 这也是识别性能瓶颈的一种特别有效的方法。
本教程使用OpenTelemetry (OTel) 的跟踪工具包,这是一个用于收集、处理和导出遥测数据的开源供应商中立标准,正在迅速普及。 在 OTel 的构想中,跟踪将涉及多个服务的数据流分割成一系列按时间顺序排列的“块”,可以帮助您轻松理解:
如果您不熟悉 OTel,请参阅什么是 OpenTelemetry?以获得有关该标准的详细介绍以及实施该标准的注意事项。
本教程重点介绍如何使用 OTel 跟踪微服务应用程序的操作。 在本教程的四个挑战中,您将学习如何通过系统跟踪请求并回答有关微服务的问题:
这些挑战说明了我们首次设置跟踪时推荐的流程。 步骤如下:
笔记: 本教程的目的是说明有关遥测的一些核心概念,而不是展示在生产中部署微服务的正确方法。 虽然它使用了真正的“微服务”架构,但仍有一些重要的注意事项:
该图说明了本教程中使用的微服务和其他元素之间的总体架构和数据流。
这两个微服务是:
三个支持基础设施是:
暂时不考虑 OTel,我们可以集中精力关注我们正在追踪的事件序列:当用户发送新的聊天消息并且收件人收到通知时会发生什么。
流程分解如下:
同时:
在设置遥测仪器时,最好从一组更明确的仪器目标开始,而不是“发送所有内容并希望获得见解”。 本教程有三个关键遥测目标:
请注意,这些目标与系统的技术操作和用户体验都相关。
要在您自己的环境中完成本教程,您需要:
Linux/Unix 兼容环境
笔记: 本教程中涉及跟踪 NGINX 的活动不适用于基于 ARM 的处理器,因为 NGINX 的 OpenTelemetry 模块不兼容。 (这包括 Linux aarch64 架构和带有 M1 或 M2 芯片的 Apple 机器。) 涉及信使和通知服务的活动适用于所有架构。
bash
有基本的了解(但提供并解释了所有代码和命令,因此即使知识有限,您仍然可以成功)Node.js 19.x 或更高版本
curl
(大多数系统上已安装)笔记: 本教程使用 JavaScript SDK,因为信使和通知服务是用 Node.js 编写的。 您还可以设置 OTel自动检测功能(也称为自动检测),以便了解 OTel 提供的信息类型。 本教程解释了有关 OTel Node.js SDK 所需了解的所有内容,但有关更多详细信息,请参阅OTel 文档。
在您的主目录中,创建microservices-march目录并将本教程的 GitHub 存储库克隆到其中。 (您也可以使用不同的目录名称并相应地调整说明。)
笔记: 在整个教程中,省略了 Linux 命令行上的提示,以便更容易地将命令复制并粘贴到终端中。 波浪号 ( ~
) 代表您的主目录。
mkdir ~/microservices-marchcd ~/microservices-march
git 克隆 https://github.com/microservices-march/messenger --branch mm23-metrics-start
git 克隆 https://github.com/microservices-march/notifier --branch mm23-metrics-start
git 克隆 https://github.com/microservices-march/platform --branch mm23-metrics-start
在本次挑战中,您将启动信使服务并配置 OTel 自动仪表以将遥测数据发送到控制台。
切换到平台存储库并启动 Docker Compose:
cd ~/microservices-march/platformdocker compose up -d --build
这将启动 RabbitMQ 和 Jaeger,它们将在后续的挑战中使用。
‑d
标志指示 Docker Compose 在容器启动时与容器分离(否则容器将保持连接到您的终端)。--build
标志指示 Docker Compose 在启动时重建所有映像。 这可以确保您运行的图像在文件发生任何潜在变化时保持更新。切换到messenger存储库中的应用程序目录并安装 Node.js(如果您愿意,可以替换为其他方法):
cd ~/microservices-march/messenger/appasdf 安装
安装依赖项:
npm 安装
启动信使服务的 PostgreSQL 数据库:
docker compose up -d
创建数据库模式和表并插入一些种子数据:
npm 运行刷新数据库
使用 OTel 自动检测,您无需修改信使代码库中的任何内容来设置跟踪。 所有跟踪配置都定义在脚本中,并在运行时导入到 Node.js 进程中,而不是导入到应用程序代码本身中。
在这里,您可以配置信使服务的自动检测,其最基本的跟踪目的地是控制台。 在挑战 2中,您将更改配置以将跟踪发送到 Jaeger 作为外部收集器。
仍然在messenger repo 的应用程序目录中工作,安装核心 OTel Node.js 包:
npm 安装 @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入门指南并浏览OTel GitHub存储库。
创建一个名为tracing.mjs的新文件,包含 OTel 跟踪的设置和配置代码:
触摸追踪.mjs
在您喜欢的文本编辑器中,打开tracing.mjs并添加以下代码:
//1
从“@opentelemetry/sdk-node”导入 opentelemetry;
从“@opentelemetry/auto-instrumentations-node”导入 { getNodeAutoInstrumentations };
//2
const sdk = new opentelemetry.NodeSDK({
traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
Instrumentations: [getNodeAutoInstrumentations()],
});
//3
sdk.start();
该代码执行以下操作:
创建 NodeSDK 的新实例并将其配置为:
ConsoleSpanExporter
)。使用自动仪器作为仪器的基础集。 该仪器加载了所有最常见的自动仪器库。 本教程中相关的内容有:
@opentelemetry/instrumentation-pg
用于 Postgres 数据库库 ( pg
)@opentelemetry/instrumentation-express
用于 Node.js Express 框架@opentelemetry/instrumentation-amqplib
用于 RabbitMQ 库 ( amqplib
)启动信使服务,导入您在步骤 3 中创建的自动检测脚本。
节点 --import ./tracing.mjs 索引.mjs
片刻之后,大量与跟踪相关的输出开始出现在控制台(您的终端)中:
...
{
跟踪 ID: '9c1801593a9d3b773e5cbd314a8ea89c',
parentId: 未定义,
traceState: 未定义,
name: 'fs statSync',
id: '2ddf082c1d609fbe',
种类: 0,
时间戳: 1676076410782000,
持续时间: 3,
属性:{},
状态:{ 代码: 0 },
事件:[],
链接:[]
}
...
笔记: 保持终端会话开放,以便在挑战 2 中重复使用。
您可以使用许多工具来查看和分析跟踪,但本教程使用Jaeger 。 Jaeger 是一个简单的、开源的端到端分布式跟踪框架,具有内置的基于 Web 的用户界面,用于查看跨度和其他跟踪数据。 平台存储库中提供的基础设施包括 Jaeger(您在挑战 1 的第 1 步中启动了它),因此您可以专注于分析数据,而不是处理复杂的工具。
您可以通过浏览器中的http://localhost:16686端点访问 Jaeger,但如果您现在访问该端点,则看不到有关您的系统的任何信息。 这是因为您当前正在收集的跟踪正在被发送到控制台! 要在 Jaeger 中查看跟踪数据,您需要使用OpenTelemetry 协议(OTLP) 格式导出跟踪。
在此挑战中,您将通过配置以下检测来检测核心用户流程:
提醒一下,使用 OTel 自动检测意味着您无需修改 Messenger代码库中的任何内容来设置跟踪。 相反,所有跟踪配置都在运行时导入到 Node.js 进程的脚本中。 在这里,您可以将信使服务生成的跟踪的目标从控制台更改为外部收集器(本教程中为 Jaeger)。
仍然在与挑战 1 相同的终端中工作,并在messenger repo 的app目录中安装 OTLP 导出器 Node.js 包:
npm 安装@opentelemetry/exporter-trace-otlp-http@0.36.0
@opentelemetry/exporter-trace-otlp-http
库通过 HTTP 以 OTLP 格式导出跟踪信息。 它用于将遥测数据发送到 OTel 外部收集器时。
打开tracing.mjs (您在挑战 1 中创建和编辑的)并进行以下更改:
将此行添加到文件顶部的导入
语句集中:
从“@opentelemetry/exporter-trace-otlp-http”导入 {OTLPTraceExporter};
将您提供给 OTel SDK 的“导出器”从挑战 1 中使用的控制台导出器更改为可以通过 HTTP 将 OTLP 数据发送到 OTLP 兼容收集器的导出器。 代替:
traceExporter:新的opentelemetry.tracing.ConsoleSpanExporter(),
和:
traceExporter:新的OTLPTraceExporter({headers:{}}),
笔记: 为了简单起见,本教程假设收集器位于默认位置http://localhost:4318/v1/traces 。 在实际系统中,明确设置位置是一个好主意。
按Ctrl+c
停止您在配置 OTel 自动仪器发送到控制台的第 4 步中在此终端中启动的信使服务。 然后重新启动它以使用步骤 2 中配置的新导出器:
^cnode --import ./tracing.mjs index.mjs
启动第二个单独的终端会话。 (后续说明将其称为客户端终端,将步骤 1 和 3 中使用的原始终端称为通讯终端。) 等待大约十秒钟,然后向信使服务发送健康检查请求(如果您想查看多条跟踪,可以运行几次):
curl -X 获取 http://localhost:4000/health
发送请求之前等待十秒钟有助于更容易地找到您的跟踪,因为它是在服务启动时自动检测生成的许多跟踪之后出现的。
在浏览器中,访问 Jaeger UI http://localhost:16686并验证 OTLP 导出器是否按预期工作。 单击标题栏中的“搜索” ,然后从服务字段的下拉菜单中选择名称以unknown_service开头的服务。 单击“查找痕迹”按钮:
单击窗口右侧的某个轨迹可以显示其中的跨度列表。 每个跨度描述作为跟踪的一部分运行的操作,有时涉及多个服务。 屏幕截图中的jsonParser跨度显示了运行信使服务的请求处理代码的jsonParser
部分所花费的时间。
如步骤 5 所述,OTel SDK 导出的服务名称 ( unknown_service ) 没有意义。 为了解决此问题,请在通讯终端中按Ctrl+c
停止通讯服务。 然后安装更多 Node.js 包:
^c
npm 安装 @opentelemetry/semantic-conventions@1.10.0 \
@opentelemetry/resources@1.10.0
这两个库提供以下功能:
@opentelemetry/semantic-conventions
— 定义 OTel 规范中定义的跟踪的标准属性。@opentelemetry/resources
– 定义一个对象(资源),代表生成 OTel 数据的源(在本教程中为信使服务)。在文本编辑器中打开tracing.mjs并进行以下更改:
将这些行添加到文件顶部的导入
语句集中:
从“@opentelemetry/resources”导入{Resource};从“@opentelemetry/semantic-conventions”导入{SemanticResourceAttributes};
通过在最后一个导入
语句后添加以下行,在 OTel 规范中的正确键下创建名为messenger
的资源
:
const resource = new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: "messenger",
});
通过在黑色行之间添加以橙色突出显示的行,将资源
对象传递给 NodeSDK 构造函数:
const sdk = new opentelemetry.NodeSDK({资源, traceExporter:new OTLPTraceExporter({ 标头: {} }), 仪器:[getNodeAutoInstrumentations()], });
重新启动信使服务:
节点 --import ./tracing.mjs索引.mjs
等待大约十秒钟,然后在客户端终端(您在步骤 4 中打开的)向服务器发送另一个健康检查请求(如果您想查看多个跟踪,可以运行该命令几次):
curl -X 获取 http://localhost:4000/health
笔记: 保持客户端终端开放以供在下一部分中重复使用,并保持通讯终端开放以供在挑战 3 中重复使用。
确认浏览器中的 Jaeger UI 中出现了一个名为messenger的新服务(这可能需要几秒钟,并且您可能需要刷新 Jaeger UI):
从服务下拉菜单中选择messenger ,然后单击查找痕迹按钮以查看来自messenger服务的所有最近痕迹(屏幕截图显示了 20 条痕迹中最近的 2 条):
单击一条轨迹可以显示其中的跨度。 每个跨度都被正确标记为源自信使服务:
现在启动并配置通知服务的自动检测,运行与前两节中信使服务基本相同的命令。
打开一个新的终端会话(在后续步骤中称为通知程序终端)。 切换到通知程序存储库中的应用程序目录并安装 Node.js(如果您愿意,可以替换为其他方法):
cd ~/microservices-march/notifier/appasdf install
安装依赖项:
npm 安装
启动通知服务的 PostgreSQL 数据库:
docker compose up -d
创建数据库模式和表并插入一些种子数据:
npm 运行刷新数据库
安装 OTel Node.js 包(有关包功能的描述,请参阅配置发送到控制台的 OTel 自动仪器中的步骤 1 和 3):
npm 安装 @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
创建一个名为tracing.mjs的新文件:
触摸追踪.mjs
在您喜欢的文本编辑器中,打开tracing.mjs并添加以下脚本以启动并运行 OTel SDK:
从“@opentelemetry/sdk-node”导入 opentelemetry;
从“@opentelemetry/auto-instrumentations-node”导入 { getNodeAutoInstrumentations };
从“@opentelemetry/exporter-trace-otlp-http”导入 { OTLPTraceExporter };
从“@opentelemetry/resources”导入 { Resource };
从“@opentelemetry/semantic-conventions”导入 { SemanticResourceAttributes };
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "notifier",
});
const sdk = new opentelemetry.NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ headers: {} }),
Instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
笔记: 此脚本与信使服务中的脚本完全相同,只是SemanticResourceAttributes.SERVICE_NAME
字段中的值为notifier
。
使用 OTel 自动检测启动通知服务:
节点 --import ./tracing.mjs索引.mjs
等待大约十秒钟,然后在客户端向通知服务发送健康检查请求。 此服务正在监听端口 5000,以防止与监听端口 4000 的通讯服务发生冲突:
curl http://localhost:5000/health
笔记: 保持客户端和通知器终端开放,以便在挑战 3 中重复使用。
确认浏览器中的 Jaeger UI 中出现了一个名为notifier的新服务:
对于 NGINX,您可以手动设置跟踪,而不是使用 OTel 自动检测方法。 目前,使用 OTel 检测 NGINX 的最常见方式是使用用 C 编写的模块。第三方模块是 NGINX 生态系统的重要组成部分,但它们需要一些工作来设置。 本教程将为您完成设置。 有关背景信息,请参阅我们博客上的为 NGINX 和 NGINX Plus 编译第三方动态模块。
启动一个新的终端会话( NGINX 终端),将目录更改为messenger存储库的根目录并创建一个名为load-balancer的新目录,以及名为Dockerfile 、 nginx.conf和opentelemetry_module.conf的新文件:
cd ~/microservices-march/messenger/mkdir load-balancer
cd load-balancer
touch Dockerfile
touch nginx.conf
touch opentelemetry_module.conf
在您喜欢的文本编辑器中,打开Dockerfile添加以下内容(注释解释了每行的作用,但您无需理解所有内容即可构建和运行 Docker 容器):
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 运行 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. 在主 NGINX 配置文件顶部安装并添加“load_module”指令&& /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
打开nginx.conf并添加以下内容:
事件 {}
http {
包括 /etc/nginx/conf.d/opentelemetry_module.conf;
上游信使 {
服务器 localhost:4000;
}
服务器 {
监听 8085;
位置 / {
proxy_pass http://messenger;
}
}
}
这个非常基本的NGINX 配置文件告诉 NGINX :
笔记: 这与 NGINX 在生产环境中作为反向代理和负载均衡器的实际配置非常接近。 唯一的主要区别是, upstream
块中服务器
指令的参数通常是域名或 IP 地址,而不是localhost
。
打开opentelemetry_module.conf并添加以下内容:
NginxModuleEnabled ON;NginxModuleOtelSpanExporter otlp;
NginxModuleOtelExporterEndpoint localhost:4317;
NginxModuleServiceName messenger-lb;
NginxModuleServiceNamespace MicroservicesMarchDemoArchitecture;
NginxModuleServiceInstanceId DemoInstanceId;
NginxModuleResolveBackends ON;
NginxModuleTraceAsError ON;
构建包含 NGINX 以及 NGINX OTel 模块的 Docker 镜像:
docker build-t messenger-lb 。
启动 NGINX 反向代理和负载均衡器的 Docker 容器:
docker run --rm --name messenger-lb -p 8085:8085 --network="host" messenger-lb
在客户端,通过 NGINX 反向代理和负载均衡器向消息服务发送健康检查请求(发送此请求前无需等待):
curl http://localhost:8085/health
笔记: 保持 NGINX 和客户端终端开放,以便在挑战 3 中重复使用。
在浏览器中,确认新的messenger-lb服务与您之前启动的服务一起列在 Jaeger UI 中。 您可能需要在浏览器中重新加载 Jaeger UI。
在架构和用户流中,我们概述了用户流的各个阶段,但回顾一下:
实施遥测的目标是:
在本次挑战中,您将学习如何评估 OTel 仪器生成的跟踪是否满足上述目标。 首先,您让系统运转起来并创建一些痕迹。 然后,您检查消息流的跟踪以及由NGINX 、信使服务和通知服务生成的消息流的各个部分。
在客户端终端中,建立对话并在两个用户之间发送几条消息:
curl -X POST \
-H“内容类型:应用/json”\
-d'{“participant_ids”:[1,2]}' \
' <a href="http://localhost:8085/conversations">http://localhost:8085/对话</a>'
curl -X POST \
-H "用户 ID: 1英寸 \
-H“内容类型:应用/json”\
-d'{“内容”: “这是第一条消息”}' \
'http://localhost:8085/conversations/1/messages'
curl -X POST \
-H “用户 ID: 2英寸 \
-H“内容类型:应用/json”\
-d'{“内容”: “这是第二条消息”}' \
'http://localhost:8085/conversations/1/messages'
通知程序服务会生成如下输出,并显示在通知程序终端中:
收到新消息:{"type":"new_message","channel_id":1,"user_id":1,"index":1,"participant_ids":[1,2]}通过短信向 12027621401 发送新消息通知
收到新消息:{"type":"new_message","channel_id":1,"user_id":2,"index":2,"participant_ids":[1,2]}
通过电子邮件向 the_hotstepper@kamo.ze 发送新消息通知
通过短信向 19147379938 发送新消息通知
在浏览器中打开 Jaeger UI,从服务下拉菜单中选择messenger-lb ,然后单击查找痕迹按钮。 出现一个跟踪列表,从流程的最开始处开始。 单击任何跟踪即可显示有关它的详细信息,如下图所示:
点击并进行一些探索。 然后在继续之前,请考虑一下跟踪中的信息如何支持挑战 3介绍中所列出的检测目标。 相关问题包括:
从 NGINX span 开始,其父 span 内有 11 个子 span。 由于当前 NGINX 配置非常简单,子跨度不是很有趣,只是显示 NGINX 请求处理生命周期中每个步骤所花费的时间。 然而,父跨度(第一个)包含一些有趣的见解:
在标签下,您会看到以下属性:
http.method
字段 – POST
(在REST术语中,这意味着创建)http.status_code
字段 –201
(表示创建成功)http.target
字段 – dialogues/1/messages
(消息端点)综合起来,这三条信息可以说明: “一个POST
请求被发送到/conversations/1/messages
,响应是201
(创建成功)”。 这对应于架构和用户流程中的步骤 1 和 4a)。
webengine.name
字段显示这是请求的 NGINX 部分。此外,由于messenger和notifier的跨度嵌套在messenger-lb dialogues/1
跨度内(如准备读取跟踪中的屏幕截图所示),您可以知道通过 NGINX 反向代理发送到messenger服务的请求击中了流中所有预期的组件。
此信息满足目标,因为您可以看到 NGINX 反向代理是流程的一部分。
在标有messenger-lb的跨度列表中,查看最近的跨度(位于列表底部),以查看请求的 NGINX 部分花费了多长时间。 在屏幕截图中,跨度从 589 微秒 (µs) 开始,持续 24µs,这意味着完整的反向代理操作仅需 613µs - 大约 0.6 毫秒 (ms)。 (当您自己运行教程时,确切的值当然会有所不同。)
在这样的设置中,大多数值仅相对于其他测量值有用,并且它们在不同系统之间有所不同。 不过,在这种情况下,这个操作的时间显然不会超过五秒钟。
这些信息满足了目标,因为您可以看到 NGINX 操作所用的时间并不接近五秒钟。 如果流程中有一个非常缓慢的操作,那么它一定会在稍后发生。
NGINX 反向代理层不包含有关此内容的任何信息,因此您可以继续使用Messenger跨度。
该跟踪的信使服务部分包含另外 11 个跨度。 同样,大多数子跨度涉及 Express 框架在处理请求时使用的基本步骤,并且不是很有趣。 然而,父跨度(第一个)再次包含一些有趣的见解:
在标签下,您会看到以下属性:
http.method
字段 – POST
(同样,在 REST 术语中,这意味着创建)http.route
字段 – /conversations/:conversationId/messages
(消息路由)http.target
字段 – /conversations/1/messages
(消息端点)此信息满足目标,因为它向我们展示了信使服务是流程的一部分,并且端点命中是新的消息端点。
如下面的屏幕截图所示,跟踪的信使部分从 1.28 毫秒开始,到 36.28 毫秒结束,总时间为 35 毫秒。 大部分时间都花在解析 JSON(中间件
-
jsonParser
)上,并且更大程度上花在连接数据库( pg-pool.connect
和tcp.connect
)上。
这是有道理的,因为在编写消息的过程中也会进行多个 SQL 查询。 这反过来表明您可能想要增强自动检测配置来捕获这些查询的时间。 (本教程未展示这种额外的检测,但在挑战 4 中,您可以手动创建跨度,然后可以用来包装数据库查询。)
该信息满足了目标,因为它表明信使操作不会花费接近五秒钟的时间。 如果流程中有一个非常缓慢的操作,那么它一定会在稍后发生。
与 NGINX 跨度一样,信使跨度不包含此信息,因此您可以转到通知器跨度。
跟踪的通知程序部分仅包含两个跨度:
chat_queue
进程
跨度 – 确认通知服务已处理来自chat_queue消息队列的事件pg-pool.connect
范围 – 显示在处理事件后,通知服务与其数据库建立了某种连接从这些跨度获得的信息仅能部分实现理解每个步骤的目标。 您可以看到通知服务已到达从队列中使用事件的阶段,但您不知道:
这表明您需要执行以下操作才能完全理解通知程序服务流程:
查看通知程序服务跨度的总体时间,您会发现请求在流程的通知程序部分花费了 30.77 毫秒。 但是,由于没有任何跨度表示整个流程的“结束”(向收件人发送通知),因此您无法确定此部分流程的总时间或操作的总体完成时间。
但是,您可以看到,通知服务chat_queue
进程
跨度在 6.12ms 处启动,比信使服务chat_queue
发送
跨度在 4.12ms 处启动晚 2ms。
这个目标已经实现,因为您知道通知程序在消息服务发送事件 2 毫秒后就消耗了该事件。 与目标 2 不同,实现此目标不需要您知道事件是否已被完全处理或花费了多长时间。
根据我们对当前 OTel 自动检测生成的跟踪的分析,很明显:
许多这些 span 在当前形式下是无用的:
请求
处理程序
和所有数据库操作的跨度。 一些中间件范围(例如expressInit
和corsMiddleware
)似乎不相关,可以被删除。下列内容缺少关键跨度:
这意味着基本仪器实现了最后一个目标:
然而,没有足够的信息来实现前两个目标:
在本次挑战中,您将根据在挑战 3 中进行的跟踪分析来优化 OTel 仪器。 这包括删除不必要的跨度、创建新的自定义跨度,以及确认通知服务所使用的事件是由信使服务生成的事件。
在您喜欢的文本编辑器中,打开messenger存储库app目录中的tracing.mjs文件,并在顶部的导入语句列表末尾添加以下内容:
const IGNORED_EXPRESS_SPANS = new Set([ "中间件 - expressInit",
"中间件 - corsMiddleware",
]);
这定义了一组跨度名称,这些名称源自 Jaeger UI 中以下屏幕截图中显示的跨度列表,因为它们没有为此流程提供有用的信息,因此将从跟踪中省略。 您可能会认为屏幕截图中列出的其他跨度也是不需要的,并将它们添加到IGNORED_EXPRESS_SPANS
列表中。
通过更改以橙色突出显示的行,将过滤器添加到自动检测配置中以省略您不想要的跨度:
const sdk = new opentelemetry.NodeSDK({资源, traceExporter:new OTLPTraceExporter({ 标头: {} }), 仪器:[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
函数引用步骤 1 中定义的跨度集,以将它们从@opentelemetry/instrumentation-express
生成的跟踪中过滤掉。 换句话说,对于属于IGNORED_EXPRESS_SPANS
的跨度, return
语句解析为true
,并且ignoreLayers
语句会从跟踪中删除该跨度。
在通讯终端中,按Ctrl+c
停止通讯服务。 然后重新启动它:
^cnode --import ./tracing.mjs index.mjs
等待大约十秒钟,然后在客户端发送一条新消息:
curl -X POST \ -H "用户 ID: 2英寸 \
-H“内容类型:应用/json”\
-d'{“内容”: “这是第二条消息”}' \
'http://localhost:8085/conversations/1/messages'
在 Jaeger UI 中重新检查messenger spans。两个中间件
spans expressInit
和corsMiddleware
不再出现(您可以将其与挑战 3 中检查跟踪的 messenger 部分的目标 2 的屏幕截图进行比较)。
在本节中,您将第一次接触应用代码。 自动检测不需要改变应用就能生成大量信息,但有些见解只有通过检测特定的业务逻辑才有可能获得。
对于您正在检测的新消息流,一个例子是跟踪向消息接收者发送通知的过程。
打开通知程序存储库app目录中的index.mjs 。 该文件包含该服务的所有业务逻辑。 在文件顶部的导入
语句列表末尾添加以下行:
从“@opentelemetry/api”导入{trace};
替换此代码(位于文件第 91 行左右):
for (let pref of preference) { console.log(`通过发送新消息通知${pref.address_type}到${pref.address}` ); }
和:
const tracer = trace.getTracer("notifier"); // 1tracer.startActiveSpan( // 2 "notification.send_all", { 属性: { user_id: msg.user_id, }, }, (parentSpan) => { for (let pref of preference) { tracer.startActiveSpan( // 3 "notification.send", { 属性: { // 4 notification_type: pref.address_type, user_id: pref.user_id, }, }, (span) => { console.log( `通过 发送新消息通知${pref.address_type}到${pref.address}` ); span.end(); // 5 } ); } parentSpan.end(); // 6 } );
新代码执行以下操作:
跟踪器
,它是与 OTel 跟踪交互的全局对象。notification.send_all
的新父跨度,并设置user_id
属性来识别消息的发送者。notification.send_all
下创建一个名为notification.send
的新子跨度。 每个通知都会生成一个新的跨度。为子跨度设置更多属性:
notification_type
–短信
或电子邮件
之一user_id
– 接收通知的用户的 ID通知发送
跨度。通知.send_all
跨度。拥有父跨度可以保证即使未发现用户的通知偏好,也会报告每个“发送通知”操作。
在通知程序终端中,按Ctrl+c
停止通知程序服务。 然后重新启动:
^cnode --import ./tracing.mjs index.mjs
等待大约十秒钟,然后在客户端发送一条新消息:
curl -X POST \ -H "用户 ID: 2英寸 \
-H“内容类型:应用/json”\
-d'{“内容”: “这是第二条消息”}' \
'http://localhost:8085/conversations/1/messages'
在 Jaeger UI 中重新检查通知器跨度。您会看到父跨度和两个子跨度,每个跨度都有一个“发送通知”操作:
现在您可以完全实现第一个和第二个目标,因为您可以看到请求在新消息流中经历的所有步骤。 每个跨度上的时间显示了这些步骤之间的任何滞后。
为了全面了解流程,您还需要做一件事。 通知服务处理的事件实际上是信使服务发送的事件吗?
您不必做任何明确的更改来连接这两条轨迹——但您也不想盲目地相信自动检测的魔力。
考虑到这一点,添加一些快速调试代码来验证 NGINX 服务中启动的跟踪确实与通知服务使用的跟踪相同(具有相同的跟踪 ID)。
打开messenger存储库app目录中的index.mjs文件并进行以下更改:
在顶部的导入语句列表末尾添加以下行:
从“@opentelemetry/api”导入{trace};
在现有的黑色行下方添加以橙色突出显示的行:
异步函数 createMessageInConversation(req,res) { const tracer = trace.getActiveSpan(); console.log("TRACE_ID: ", tracer.spanContext().traceId);
新行从messenger中处理新消息创建的函数内部打印出TRACE_ID
。
打开通知程序存储库app目录中的index.mjs文件,并在现有的黑色行下方添加以橙色突出显示的行:
导出异步函数 handleMessageConsume(channel, msg, handlers) { console.log("RABBIT_MQ_MESSAGE: ", msg);
新行打印通知服务收到的 AMQP 事件的完整内容。
通过在 messenger 和 notifier终端中运行以下命令来停止并重新启动messenger和notifier服务:
^cnode --import ./tracing.mjs index.mjs
等待大约十秒钟,然后在客户端再次发送一条消息:
curl -X POST \ -H "用户 ID: 2英寸 \
-H“内容类型:应用/json”\
-d'{“内容”: “这是第二条消息”}' \
'http://localhost:8085/conversations/1/messages'
查看信使和通知服务的日志。 信使服务日志包含如下一行,报告消息的跟踪 ID(运行本教程时实际 ID 会有所不同):
跟踪 ID: 29377a9b546c50be629c8e64409bbfb5
类似地,通知程序服务日志在输出中报告跟踪 ID,如下所示:
_spanContext:{
traceId: '29377a9b546c50be629c8e64409bbfb5',
spanId: 'a94e9462a39e6dbf',
跟踪标志: 1,
traceState:未定义
},
控制台中的跟踪 ID 是匹配的,但最后一步是,您可以将它们与 Jaeger UI 中的跟踪 ID 进行比较。在相关跟踪 ID 端点处打开 UI(您的端点会有所不同,但在此示例中为http://localhost:16686/trace/29377a9b546c50be629c8e64409bbfb5 )以查看整个跟踪。 Jaeger 的追踪结果证实:
笔记: 在实际的生产系统中,一旦确认流程按预期工作,您就可以删除此部分中添加的代码。
您已经在本教程中创建了一些容器和图像! 使用这些说明来删除它们。
要删除所有正在运行的 Docker 容器:
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
恭喜您,您已完成本教程!
然而,您仅仅触及了理想跟踪配置的表面! 在生产环境中,您可能希望添加诸如每个数据库查询的自定义跨度之类的内容,以及所有跨度上的附加元数据,以描述运行时详细信息(例如每个服务的容器 ID)。您还可以实现其他两种类型的 OTel 数据(指标和日志记录),以便全面了解系统的运行状况。
要继续您的微服务教育,请查看 2023 年 3 月的微服务。 在第 4 单元中: 通过可观察性管理微服务混乱和复杂性,您将了解可观察性数据的三个主要类别、基础设施和应用程序协调的重要性,以及开始分析深度数据的方法。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”