一年前,我们排查线上问题的方式是这样的:用户反馈"下单失败",我们打开日志平台,用订单号搜日志,搜出来的是一堆散落在各个服务的日志片段,得人工拼起来才能知道请求到底经过了哪些服务、卡在了哪一环。
一次完整排查平均要40分钟到1个小时。遇到大促期间问题密集,基本就是灾难。
现在我们有了全链路追踪,同样的"下单失败",点开Trace ID,整条调用链一目了然:请求从网关进来,经过订单服务、库存服务、支付服务,在哪一步耗时最长、哪一步抛了异常,全部看得清清楚楚。排查时间从1小时缩到了几分钟。
这篇把我们全链路追踪的落地过程写出来,包括选型、接入、踩坑。给准备搞的团队一个参考。
全链路追踪解决什么问题
微服务架构下,一个请求要经过多个服务,问题就来了:出了问题,你不知道问题在哪一环。
- 请求超时了,是订单服务慢,还是库存服务慢,还是数据库慢?
- 请求失败了,异常是在哪个服务抛的?
- 同一个请求在订单服务打了日志,在支付服务也打了日志,怎么把它们关联起来?
全链路追踪的核心思路:给每个请求分配一个全局唯一的Trace ID,让它贯穿所有服务。每个服务在处理这个请求时,都带上这个Trace ID记录日志。 这样就能把分散在各服务的日志串成一条完整的调用链。
配合可视化工具,你看到的是一张瀑布图:每个服务是一段,耗时、异常一目了然。
选型:我们为什么选SkyWalking
当时市面上的方案主要有三个:SkyWalking、Zipkin、Jaeger。对比了一圈:
| 方案 | 接入方式 | 优点 | 缺点 |
|---|---|---|---|
| SkyWalking | Java Agent,无代码侵入 | 自动探针,部署简单,UI功能全 | 有一定学习成本 |
| Zipkin | SDK埋点 | 轻量 | 需要手动埋点,接入成本高 |
| Jaeger | SDK埋点 | 性能好 | 同样需要手动埋点 |
我们最终选了SkyWalking,核心原因就一个:Java Agent无侵入。 不需要改业务代码,加上一个Agent参数就能自动采集Spring Boot、Feign、数据库、Redis、消息队列的调用链数据。对存量系统来说,这个优势是碾压级的。
bash
# 启动参数加上SkyWalking Agent,其他什么都不用改
java -javaagent:/opt/skywalking/agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-jar order-service.jar
落地步骤
第一步:部署SkyWalking后端
SkyWalking包括三部分:OAP Server(收集和存储数据)、UI(可视化界面)、Agent(嵌入应用采集数据)。
我们用Docker Compose起了OAP和UI,一套搞定:
yaml
services:
oap:
image: apache/skywalking-oap-server:9.7.0
ports:
- "11800:11800" # gRPC采集端口
- "12800:12800" # HTTP查询端口
ui:
image: apache/skywalking-ui:9.7.0
ports:
- "8080:8080"
environment:
SW_OAP_ADDRESS: oap:12800
Agent的数据通过gRPC(11800端口)上报到OAP,UI通过HTTP(12800端口)查数据。
第二步:所有服务接入Agent
每个服务的启动参数加上Agent,service_name改成对应服务名。这一步是纯配置,不改任何代码:
bash
java -javaagent:/opt/skywalking/agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-jar order-service.jar
网关、订单、库存、支付、积分,所有服务都加上,链路就通了。
第三步:日志关联Trace ID
这是让排查效率翻倍的关键一步。SkyWalking会自动生成Trace ID,但这个ID在业务日志里默认看不到。
通过日志框架的MDC(Mapped Diagnostic Context)把Trace ID注入到日志里:
xml
<!-- logback配置:在日志格式里加一个traceId占位符 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
再配合SkyWalking Agent,它会自动把Trace ID塞进MDC:
yaml
# 在agent配置里开启日志关联
plugin:
logback:
pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{tid}] %-5level %logger{36} - %msg%n"
这样日志平台里每条日志都带Trace ID,搜Trace ID能搜出整条链路的日志,点Trace ID能看可视化链路图。 两者打通之后,排查效率才真正起飞。
踩过的坑
落地过程不是一帆风顺的,这几个坑值得说。
坑1:Agent版本必须和服务端匹配。 我们用9.x的Agent配9.x的OAP没问题,但后来升级UI时不小心把OAP升到了10.x,Agent还是9.x,结果采集数据全丢了,UI上啥都看不到。升级时必须Agent和OAP一起升,版本要匹配。
坑2:Agent对性能有损耗,注意压测。 加了Agent之后,每个请求都要采集和上报数据,理论上会有性能损耗。我们在测试环境压测过,P99损耗在3%-5%左右,可接受。但如果你本来就卡在性能边缘,这个损耗要考虑进去。
坑3:采样率别拉满。 全量采集数据量很大,存储成本高,而且压垮OAP。我们的策略是:核心链路100%采样,非核心链路10%采样。 SkyWalking支持按服务配置采样率。
坑4:异步线程和消息队列的链路要单独处理。 我们一开始发现,有些请求的链路在异步任务那里断了------Trace ID没传过去。原因是线程池里的新线程不带上下文。SkyWalking对@Async和RocketMQ有自动支持,但自定义线程池需要手动传递上下文,这个要单独验证。
落地后的实际效果
一年下来,最直观的变化:
排查时间: 平均40分钟 → 5分钟。以前拼日志,现在看链路图,定位慢接口和异常一步到位。
架构梳理: 全链路追踪的拓扑图(哪些服务调了哪些服务、调用量多少)成了我们架构评审的必备材料,新同学看拓扑图就能快速理解系统。
容量规划: 追踪数据里的耗时分布,帮我们定位了好几个隐藏的慢调用------比如某个服务调第三方接口没设超时,平时不显眼,压测时才暴露,追踪数据直接把它揪出来了。
大促保障: 大促期间我们建了个监控大屏,实时看全链路各环节的耗时和错误率,哪个环节顶不住了立刻知道。
结尾
全链路追踪这东西,不上线之前觉得"可有可无",上了线就回不去了。它不是锦上添花的监控工具,而是微服务架构下的基础设施------排查问题、梳理架构、容量规划、大促保障,全都靠它。
如果你还在用"翻日志拼链路"的方式排查问题,强烈建议搞一套。SkyWalking是开源免费的,Agent无侵入,投入产出比极高。
如果这篇对你有用,点个关注。后面我还会写更多微服务治理和性能优化的实战内容。