日常工作中很多时候都会讨论微服务可观测性的话题,但在实际项目中落地实施仍然非常困难,经常受限与时间、服务器资源等问题无法落地,正好最近一段时间抽身出来完成了一个微服务可观测性全栈实践,希望能给大家带来一些参考。
一、方案选型背景
为什么不选 SkyWalking?
- 错误识别问题:项目内部将异常统一封装为业务
code错误码返回,HTTP 状态码始终为 200。SkyWalking 基于 HTTP 状态码判断请求是否出错,无法识别业务层面的错误,导致错误率指标完全失真。 - 资源开销大:SkyWalking Agent 的 Java Agent 机制会拦截大量字节码,在高并发 Dubbo 调用场景下 CPU 和内存开销显著,对业务性能有可感知的影响。
相比之下,Sleuth + Tempo 方案足够轻量,且错误判定完全由应用侧通过 Span Tag 自定义,不受 HTTP 状态码限制。
为什么不选 Ctld?
我也尝试接入 Ctld,但 Ctld 的版本依赖与当前技术栈(Spring Boot 2.7.18 / Spring Cloud 2021.0.8)存在兼容性冲突,强行适配成本过高,故放弃。
二、整体架构
三条数据线:
| 数据线 | 采集端 | 传输通道 | 存储 | 可视化 |
|---|---|---|---|---|
| Traces | Sleuth + Brave-Dubbo | → Tempo(Zipkin 协议直连) | Tempo | Grafana |
| Metrics | Tempo Metrics Generator | → Prometheus | Prometheus | Grafana |
| Logs | Promtail | → Loki | Loki | Grafana |
三、版本信息与 Maven 依赖
1 | <properties> |
3.1 链路追踪依赖(各微服务引入)
1 |
|
3.2 应用配置
1 | # application.yml |
3.3 Dubbo 配置
1 | # application.yml |
braveConsumerFilter/braveProviderFilter由brave-instrumentation-dubbo提供,自动在 Dubbo 调用时传递 traceId/spanId。
3.4 Logback 日志配置(关键部分)
1 |
|
核心要点:
%X{traceId}/%X{spanId}是 Sleuth 自动注入 MDC 的变量,Logback 通过%X{}取出并打印到每条日志中,这是后续 Promtail 解析 traceId 并与 Tempo 关联的基础。
四、链路追踪:Sleuth → Tempo(Zipkin 协议直连)
4.1 数据流
1 | 微服务(Sleuth 生成 traceId/spanId) |
Tempo 原生内置 Zipkin Receiver,无需额外部署 Zipkin Server,Sleuth 直接上报即可。
4.2 Tempo 关键配置
1 | # tempo.yaml |
4.3 验证 Trace 链路
- 启动微服务后,发起一次包含 Dubbo 调用的请求
- 查看日志中的
[service-name,traceId,spanId]是否有值 - Grafana 添加 Tempo Data Source(
http://tempo:3200),用 traceId 搜索调用链
五、指标体系:Tempo → Prometheus → Grafana
5.1 QPS / 错误率 / 慢请求 的来源
Tempo 的 Metrics Generator 会根据 Span 数据自动聚合生成 RED 指标(Rate / Error / Duration):
| 指标名 | 含义 |
|---|---|
traces_spanmetrics_calls_total |
总请求数(QPS = rate) |
traces_spanmetrics_calls_total{status_code="error"} |
错误请求数 |
traces_spanmetrics_latency_bucket |
请求延迟分布(Histogram) |
5.2 Prometheus 记录规则(可选,简化查询)
1 | # prometheus-rules.yml |
5.3 Grafana 面板 PromQL
QPS:
1 | sum(rate(traces_spanmetrics_calls_total{service_name=~"$service"}[1m])) |
错误率:
1 | sum(rate(traces_spanmetrics_calls_total{service_name=~"$service",status_code="error"}[1m])) |
慢请求率(耗时 > 1s 为慢请求):
1 | ( |
P99 延迟:
1 | histogram_quantile(0.99, |
六、日志聚合:Promtail → Loki
6.1 数据流
1 | 微服务日志文件(/var/log/*.log,含 traceId) |
6.2 Promtail 配置
1 | # promtail-config.yaml |
6.3 Tempo ↔ Loki 关联
原理: Sleuth 在日志中注入了 traceId,Loki 通过 traceId 标签索引日志,Grafana 根据相同的 traceId 在 Tempo 和 Loki 之间跳转。
Loki Data Source 配置关键字段(Grafana):
1 | Derived fields: |
这样在 Grafana 中查看 Loki 日志时,每条日志旁边的 traceId 会变成一个可点击的链接,点击后直接跳转到 Tempo 的对应调用链视图。反之在 Tempo 的 Span 详情中也能看到 Related logs。
七、告警链路:Prometheus → Alertmanager → PrometheusAlert → 企业微信
7.1 整体链路
1 | Prometheus(告警规则触发) |
为什么引入 PrometheusAlert? Alertmanager 原生不支持企业微信。PrometheusAlert 作为中间层,接收 Alertmanager 的 webhook,按照自定义模板渲染告警内容后,通过企业微信机器人、应用消息或微信客服通道完成推送。
7.2 Prometheus 告警规则
1 | # prometheus-alert-rules.yml |
7.3 Alertmanager 配置
1 | # alertmanager.yml |
7.4 PrometheusAlert 配置要点
1 | # PrometheusAlert conf/app.conf 关键配置 |
企业微信告警模板示例(wechat-template.tpl):
1 | {{ $var := .externalURL}} |
八、Docker Compose 一键部署(参考)
1 | version: '3.8' |
九、关键验证清单
| 序号 | 验证项 | 预期结果 |
|---|---|---|
| 1 | 微服务日志中可见 [appName,traceId,spanId] |
traceId 非空 |
| 2 | Grafana Tempo Data Source 通过 traceId 检索调用链 | 调用链展示完整,含 Dubbo Span |
| 3 | Prometheus 中存在 traces_spanmetrics_* 指标 |
指标有数据点 |
| 4 | Grafana Dashboard 中 QPS / 错误率 / 慢请求 面板有数据 | 曲线正常 |
| 5 | Loki 可按 traceId 检索日志 | 返回对应 Trace 的日志行 |
| 6 | Tempo Span 详情页可看到 Related logs | 点击跳转到 Loki 日志 |
| 7 | 触发错误率 > 1% → 企业微信收到告警 | 告警内容准确 |
| 8 | 错误率恢复 → 企业微信收到恢复通知 | [Resolved] 消息正常 |
十、注意事项
- 采样率:生产环境 Sleuth 采样率建议
0.1(10%),全量采样对 Tempo 存储压力大。 - Tempo Metrics Generator:是 Tempo 的 optional 组件,需显式开启才能生成
traces_spanmetrics_*指标推送到 Prometheus。 - Tempo 直连:Tempo 内置 Zipkin Receiver(端口 9411),Sleuth 配置
zipkin.base-url指向 Tempo 即可,无需部署 Zipkin Server。 - PrometheusAlert 版本:推荐
v4.0+,对企微支持更完善。 - 日志格式:必须确保
traceId以结构化方式出现在日志中(推荐 JSON 格式,方便 Promtail pipeline 解析)。 - 关联生效前提:Grafana 中 Loki Data Source 必须正确配置 Derived fields 关联到 Tempo Data Source。
- Dubbo 过滤器:
braveConsumerFilter/braveProviderFilter必须配置,否则 Dubbo 调用不会传递 traceId,链路会断。
(注:内容由 AI 辅助编写)
[越努力,越幸运!]