
1. 先说痛点线上服务出了问题你花了多久才找到根因我印象很深的一次经历某个下午线上订单服务突然变慢用户反馈下单要转圈十几秒。我们几个后端围着日志查了快一个小时——服务A的日志显示调了服务B服务B的日志显示自己只花了200ms但服务A却等了4秒。两边各执一词数据库也没有慢SQL缓存命中率也正常。最后实在没辙只能用最笨的办法在A和B之间临时加耗时日志重新发布再复现一遍。如果你也经历过这种“明明每个环节都正常但整体就是慢得离谱”的排查噩梦那你大概率需要一套链路跟踪系统。这篇文章就聊我在SpringBoot项目里集成Skywalking的全过程从最基础的概念、环境搭建、项目接入到实际排障的用法和踩坑心得适合刚接触链路跟踪、或者已经用过但没深入折腾过的同学。Skywalking是国内开源圈子里使用率非常高的一款APM应用性能监控工具核心功能就是链路跟踪、服务拓扑分析、慢SQL追踪。相比同类工具它的最大优势是对业务代码无侵入——Java项目只需要加上探针参数启动就能自动采集调用链数据不用改一行业务代码。这期教程主要讲解怎么把Skywalking和SpringBoot项目集成起来让你在浏览器里看到一次请求从入口到数据库的完整旅程。2. 选型上的门道为什么是Skywalking而不是Zipkin或Jaeger先解决一个大家都会问的问题链路跟踪工具那么多Zipkin、Jaeger、Skywalking到底怎么选我不是说其他工具不行而是它们解决的侧重点不太一样。2.1 三类工具的典型对比Zipkin老牌链路跟踪系统Twitter开源。特点是轻量、实现简单很多中间件原生支持上报数据。但它的UI比较朴素自带功能偏少更像个“链路查询器”缺少告警、指标聚合这些偏运维的能力。JaegerCNCF孵化的项目师承Google Dapper论文在云原生环境里很吃香。如果你用的是Kubernetes Istio这类Service Mesh架构Jaeger几乎是标配。不过它对非云原生场景的友好度一般。Skywalking国产开源Apache顶级项目。最大的卖点是Agent自动埋点——Java生态里主流的框架、中间件、数据库驱动几乎全覆盖不需要你手动埋点就能拿到数据。而且它自带拓扑图、告警、JVM监控、日志关联功能一体化程度很高。2.2 我的选型理由我自己在中小型微服务团队里做技术选型时核心考量有四个接入成本Zipkin和Jaeger在SpringBoot里接入需要主动引入依赖、配置spring.sleuth相关参数并且很多场景还要手动写Span。Skywalking只需要改启动脚本业务代码零改动对一个存量项目来说这个优势是压倒性的。功能完整度Skywalking不只是链路跟踪它还能展示服务之间的依赖拓扑、每个接口的响应时间分布、JVM内存/GC情况甚至SQL执行参数。一套系统解决监控和追踪两个问题省事。中文社区和文档这个不用多说Skywalking的中文资料、博客、社区讨论非常丰富遇到问题搜索一下基本都有答案。工具再强没人答疑也费劲。部署和维护成本 Skywalking服务端OAP Server Web UI就是两个进程没有额外依赖存储默认用H2文件也可以接ES、MySQL。相比之下Zipkin如果想做持久化还得配ESJaeger一般也要搭Cassandra或ES。注意如果你所在团队已经重度使用Kubernetes IstioJaeger会更契合云原生链路但如果你主要是SpringCloud、SpringBoot传统微服务Skywalking的性价比是最高的。2.3 理解Skywalking的三个核心概念在动手部署前先把三个词搞明白否则后面很多配置你会看得一头雾水Agent探针一个挂在应用进程里的JAR包负责自动采集调用链数据然后上报给OAP Server。它和业务代码完全隔离这也是Skywalking“无侵入”的关键。OAP ServerObservability Analysis Platform分析引擎接收Agent上报的数据做聚合、存储、告警计算并对外提供查询接口。通俗理解就是后端服务。SkyWalking UIWeb展示界面从OAP Server拉数据把链路、拓扑、指标可视化。类似数据库管理工具和数据库的关系。这三者的关系可以这样类比Agent是放在每个商家收银台旁边的“监控摄像头”OAP Server是汇总所有监控录像并做分析的“机房”UI就是给你看录像回放的“显示屏”。3. 环境准备搭建Skywalking服务端含版本选择Skywalking的部署方式有很多种源码起、二进制包起、Docker起、K8s helm起。为了方便演示和日常开发联调我推荐用Docker Compose快速搭建简单、干净、可重复。但正式环境如果是生产级别建议单独部署OAP并把存储切到Elasticsearch。3.1 版本选择细节我这次使用Skywalking 8.6.0对应Agent版本也是8.6.0。这里有个非常关键的匹配规则Agent和OAP Server的版本号要尽量保持一致。小版本不一致可能能跑但大版本不一致基本连不上或者数据解析出错。我踩过Agent 8.7.0配合OAP 8.6.0的坑上报数据一直看不到查了半天是版本兼容问题。另外要注意JDK版本的适配Skywalking 8.6.0的OAP Server要求JDK 8而Agent对JDK8-11的支持都挺稳。如果你的SpringBoot项目是JDK17建议用Skywalking 9.x以上的版本兼容性更好。3.2 Docker Compose快速搭建我准备了一个最简配置放到项目里的skywalking-docker-compose.yml文件version: 3 services: # Skywalking OAP Server oap: image: apache/skywalking-oap-server:8.6.0-es7 container_name: skywalking-oap restart: always ports: - 11800:11800 # Agent上报数据的gRPC端口 - 12800:12800 # UI查询数据和请求OAP的HTTP端口 environment: SW_STORAGE: h2 # 先拿H2内存存储跑起来演示够用 volumes: - ./oap-data:/tmp/skywalking-data # 持久化H2数据文件重启不丢 # Skywalking UI ui: image: apache/skywalking-ui:8.6.0 container_name: skywalking-ui restart: always depends_on: - oap ports: - 8080:8080 # 本地8080端口访问UI如果被占用改成18080:8080 environment: SW_OAP_ADDRESS: http://oap:12800在本机执行docker-compose -f skywalking-docker-compose.yml up -d等一两分钟访问http://localhost:8080如果能看到SkyWalking UI的登录页说明服务端起来了。注意生产环境不要用H2存储数据量上来后会有性能问题。建议SW_STORAGEelasticsearch并单独部署ES集群。网络热词里提到“docker部署springboot项目”时也常配合Skywalking容器一起编排如果是云环境就用docker stack或K8s管理更合适。3.3 端口说明与网络规划很多同学第一次部署容易混淆端口角色我列个表方便以后查端口所属进程用途注意事项11800OAPAgent上报链路数据的gRPC端口Agent配置里填这个端口12800OAPUI查询数据用的HTTP端口一般调试接口时才用8080UISkywalking前端页面可映射成宿主机其他端口如果公司网络策略严格需要保证应用服务器能访问OAP的11800端口UI那台机器能访问12800和8080端口。我遇到过明明UI能看到数据但Agent一直不上报的情况排查到最后发现是11800端口在安全组里没放通。4. SpringBoot项目接入两分钟完成Agent配置服务端就绪后接下来把SpringBoot项目接入Skywalking。这是整篇教程里最爽的一步因为不需要pom里加任何依赖不需要写任何配置类只需要改启动命令。4.1 下载Agent包首先到Skywalking官网下载对应版本的Agent发行包通常在agent目录下会有这些内容skywalking-agent.jarAgent的核心入口JARconfig/agent.configAgent配置文件plugins/存放各种框架的自动埋点插件optional-plugins/部分可选插件如Spring Cloud Gateway、Webflux等我习惯把Agent包解压到固定目录比如/opt/skywalking-agent这样多个项目可以复用同一份。4.2 配置Agent启动参数启动SpringBoot应用时在JVM参数里加两行java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \ -Dskywalking.agent.service_nameorder-service \ -Dskywalking.collector.backend_service127.0.0.1:11800 \ -jar order-service.jar参数含义-javaagent指定Agent JAR路径让JVM在加载业务类之前先加载探针。-Dskywalking.agent.service_name给这个服务取名会显示在UI的拓扑图和表里。建议和SpringBoot应用名保持一致比如spring.application.name的值。-Dskywalking.collector.backend_serviceOAP Server的gRPC地址和端口格式是ip:11800。agent.config文件里也有同样含义的配置项。命令行参数优先级更高所以我没有直接改文件而是通过-D传参。如果你用的是IDEA本地调试可以在VM options里加上这些参数。如果服务是通过Docker部署的就在docker run命令里增加JAVA_OPTS环境变量。4.3 验证Agent是否生效启动项目时观察控制台日志。如果看到类似下面的输出说明探针已经成功挂载SkyWalking agent [order-service] start successfully.如果没有看到检查一下-javaagent路径是否正确。日志级别不够时也可以加-Dskywalking.logging.leveldebug看详细加载过程。然后随便调用两个接口再回到UI页面服务列表里应该能看到order-service。点击进入后能看到接口的请求量、P95耗时、成功率等指标。再点击某个接口的Trace按钮就能看到这次请求的完整调用链。4.4 为什么Agent能做到无侵入这里顺带解释一下原理理解了才能用好。Skywalking Agent本质上是一个Java Agent利用JDK 1.5提供Instrumentation机制在类加载的时候动态修改字节码给目标类的方法前后插入埋点逻辑。这个过程发生在应用代码运行之前所以业务代码不需要感知。你可以把Agent想象成给电梯装的计费芯片电梯本身的控制程序不用改芯片自动感应每一层进出的人记录谁在几楼停了多久。探针做的事类似它自动“感应”Tomcat的请求入口、RestTemplate的调用、JDBC的SQL执行把这些数据串成一条链路。5. 进阶实战手动埋点、跨线程传参和MQ链路透传自动埋点覆盖了绝大多数常见场景但总有一些特殊场景需要手动介入。比如异步线程里发起的调用Skywalking默认会丢失上下文——因为链路ID存在ThreadLocal里子线程拿不到父线程的数据。5.1 用apm-toolkit手动埋点引入依赖dependency groupIdorg.apache.skywalking/groupId artifactIdapm-toolkit-trace/artifactId version8.6.0/version /dependency然后在你关心的业务方法上加注解Trace public void doBusiness(String orderId) { // 业务逻辑 }这样这个方法会成为链路里的一个Span能单独看到耗时。如果你还想在链路里带上业务参数可以在方法内部调用ActiveSpan.tag(orderId, orderId); ActiveSpan.info(开始处理订单业务);加了自定义标签后在UI里查看某条Trace的详情时就能直接看到orderId的值排障时非常管用。比如用户反馈某个订单一直失败你搜到对应耗时长的Trace直接在Span信息里看清楚是哪笔订单不用再去翻日志把TraceID和订单号关联起来。5.2 异步线程场景的处理如果业务代码里用了ExecutorService、CompletableFuture或者用了Async注解子线程里的调用默认不会被串到父链路里。解决办法是使用Skywalking提供的包装类ExecutorService executorService Executors.newFixedThreadPool(10); executorService.execute(CallableWrapper.of(() - { // 这里会继承主线程的Trace上下文 remoteService.call(); }));对应还有RunnableWrapper、SupplierWrapper包在任务外层即可。原理是在任务执行前临时把父线程的Context快照塞到子线程的ThreadLocal里任务执行完再清理掉避免内存泄漏。5.3 MQ场景的链路透传在订单系统里经常是下单接口把消息发到RocketMQ/Kafka消费者再处理。自动插件对MQ也有支持但默认只透传Header里的链路上下文。如果你用的是Spring Cloud Stream或自定义消息体需要额外设置消息头透传。以RocketMQ为例Skywalking 8.6.0插件会自动把sw8开头的链路上下文放进消息的Properties里消费者端取到后自动恢复上下文。这要求消息中间件允许设置自定义属性。如果用的某些封装框架把Properties丢了只能自己手动写ContextManager来跨线程传递不过这个情况比较少见遇到再深入排查即可。6. 在Skywalking UI里做链路分析含实际排障流程集成跑通之后最关心的就是怎么利用这些数据排查线上问题。我分享一个真实的排查过程给大家参考。6.1 拓扑图与服务依赖透视在UI首页的拓扑图模块能看到所有接入服务以及它们之间的调用关系。线条的粗细代表调用量大小颜色代表健康状态红色说明有异常。有一次我发现user-service和order-service之间有一根很粗的红线点进去查看发现order-service调用user-service接口的失败率高达30%。顺着这个线索查下去才发现是上游接口在高峰期频繁超时。如果你们系统没接Skywalking这种调用量级的异常往往要靠业务监控告警才能发现而且定位慢。有了拓扑图一眼就能看出来问题出在哪条边上。6.2 链路详情从TraceID到瓶颈定位用户反馈某个请求很慢时在Skywalking UI的追踪页面输入TraceID可以从日志里搜到即可查看完整调用链。Skywalking把每次请求拆成若干个Span每个Span记录了一个调用动作比如HTTP调用、SQL执行、Redis操作并标记耗时。我之前排查过一个“下单接口偶尔耗时3秒”的问题。从Trace详情里看到耗时集中在一次SELECT上但那条SQL如果单独在数据库客户端执行只有几十毫秒。继续下钻发现那次查询前正好发生了连接池等待——连接池里的连接都被长时间占用新请求只能排队等连接。之后再排查具体是什么SQL长时间占用连接最终定位到是统计报表的批量查询拖垮了连接池。如果没有链路数据这种场景几乎不可能靠猜定位到。6.3 慢SQL与数据库端点分析Skywalking还能自动抓取SQL执行信息。在UI的数据库模块能看到每条SQL的调用次数、平均耗时、最大耗时。对慢SQL点进去会直接显示带参数的完整SQL语句省去你自己抓慢日志、拼参数的麻烦。这套功能尤其适合没有专职DBA的团队。我用它发现过不少隐藏问题比如某条SQL在测试环境数据量小、跑得飞快上了生产后数据量大了命中索引差平均耗时飙升到几百毫秒。SQL执行参数被Skywalking记录下来了直接拿去生产库EXPLAIN分析执行计划很快就找到了缺失的联合索引。7. 常见问题速查版本匹配、数据不上报、UI空白等集成过程里我前前后后踩了不少坑。整理成一张速查表每个问题都是亲身遇到或帮别人排查过的问题现象常见原因解决方式项目启动报SkyWalking agent start successfully但UI看不到服务Agent版本和OAP版本不匹配升级/降级到同版本重新挂载启动UI能看到服务但看不到Trace数据探针挂载成功但没有触发埋点或采集器端口不通检查是否调用了自动插件支持的框架telnet测试11800端口连通性微服务A调B但链路只显示A自己的Span没有在B上也挂Agent或B的service_name配置错误在B的JVM参数中加入同样的-javaagent请求在异步线程里链路断裂子线程未继承Trace上下文用RunnableWrapper/CallableWrapper包装UI打开是空白页UI服务连不上OAP的12800端口检查SW_OAP_ADDRESS配置以及两个容器的网络是否在同一网络链路里看不到SQL某些ORM框架插件没生效查看plugins目录是否有对应框架插件必要时使用optional-plugins/apm-spring-webflux等数据量大导致OAP内存飙升存储选型不对或Agent采样率太高生产切ES存储调整SW_AGENT_SAMPLE_RATE采样率参数7.1 关于SpringBoot版本太高的兼容问题网络热词里提到“springboot版本太高”这里单独说一下。SpringBoot 2.x和Skywalking 8.x配合基本没有大坑但SpringBoot 3.x基于Spring Framework 6和Jakarta EE规范很多字节码增强的插件出现兼容性问题。如果你用的SpringBoot 3.x建议直接用Skywalking 9.x或最新版官方在9.x之后对SpringBoot 3做了更多适配。这个梗几乎每隔一段时间就有人在社区问提前避坑。7.2 Agent对性能的损耗实测不少人对探针的性能损耗有顾虑。我做过一次简单的压测对比在同样的接口上挂不挂AgentP99耗时的差异大概在3%-5%吞吐量下降也在这个范围。对于绝大多数业务系统来说这个损耗完全可以用监控价值换回来。但如果你做的是极致的低延迟系统那可以把采样率调低默认是-1全量采样可以改成20表示20%采样大幅降低上报压力。7.3 我的三条避坑心得第一先小范围试点再全量接入。不要一上来就把所有服务都挂上Agent问题排查起来容易糊。先挑一个非核心服务跑一天确认数据完整、性能影响可控再逐步推广。第二让Agent版本和OAP版本保持一致这个我再强调一遍。很多诡异问题都是因为版本不一致造成的升级Agent前一定要看官方升级文档里面有兼容性说明。第三日志的TraceID要放进Skywalking。日志关联查询是链路跟踪的隐藏价值但默认情况下业务日志里没有TraceID需要你在logback/log4j2里配置%X{tid}之类的转换符。具体做法可以参考Skywalking官方文档里的“Logback TraceId Pattern”这样排障时就能从日志直接跳到链路也能从链路反查日志。8. 后续可以做的一些扩展链路跟踪只是可观测性的一个维度。集成Skywalking跑通之后如果时间和精力允许我建议接着做这几件事一是告警配置。Skywalking内置了不少告警规则比如“接口成功率低于某个阈值”“响应时间超过某个值”等。你可以在config/alarm-settings.yml里调整规则并配置Webhook把告警推到钉钉或企业微信。有了告警监控才真正闭环。二是对接日志系统。把Skywalking和ELK或Loki联动让TraceID贯穿日志、指标、链路三个体系排查效率会再上一个台阶。三是从单体Demo过渡到微服务场景。动手搭一个SpringCloud全家桶的示例让多个服务互相调用再让Gateway统一入口你会看到拓扑图自动把整个调用网络画出来那一刻对“链路跟踪能做什么”的感受会更深。9. 写在最后另一个小技巧最后再分享一个实用小技巧如果你不想在启动命令里写很长一串-D参数可以直接修改/opt/skywalking-agent/config/agent.config里的agent.service_name和collector.backend_service两个配置项。这样多个项目复用同一份Agent包时只需要在各自的启动脚本里用-D覆盖不同的服务名即可。我在本地开发的时候就是在IDEA的.run配置里维护不同项目的VM options不需要动Agent文件怎么折腾都不会把公共配置改坏。链路跟踪这个东西接入本身不难难的是持续用它在日常排障中发挥作用。希望这篇教程能帮你从“能跑起来”走到“会用它定位问题”。实操中如果你遇到别的问题欢迎在评论区留言我看到了会尽量回复。