尧图精选

SpringBoot集成Skywalking:微服务链路追踪与性能排查实战指南

🕒 发布时间:2026/10/1 17:51:14 📁 来源:尧图网络
先讲一段真实经历。当时我负责的SpringBoot服务已经拆成了五六个微服务某天用户反馈下单很慢但每台机器上的日志都干干净净没有任何异常。我把网关、订单、库存、用户服务各自的日志文件按时间戳一条条对齐来回翻了快一个小时才在一堆无关日志里发现是某个第三方渠道的接口超时。如果当时系统里有链路跟踪从拓扑图上点一下三五分钟就能锁定问题。那次之后我就下定决心必须把Skywalking用起来——无侵入、Java Agent方式接入、SpringBoot项目几乎零改造适合绝大多数微服务团队上手。这篇聊聊SpringBoot集成Skywalking链路跟踪的完整过程包括环境部署、Agent挂载、Dashboard数据解读、生产落地配置和我在实际接入中踩过的坑。内容偏向实操适合正在给SpringBoot项目做可观测性建设或者被接口慢但查不出在哪一环折腾过的同学参考。1. 微服务慢请求排查之痛为什么我最终选了Skywalking1.1 一次线上问题让我意识到日志对齐的极限那次线上排查让我印象特别深刻。用户反馈下单慢但单次接口的耗时分布看起来并没有明显恶化只是有小部分请求超过了3秒。我们的微服务链路是网关 - 订单服务 - 库存服务 - 用户服务中间还穿插了Redis缓存和几个外部HTTP调用。每个服务都打了日志但每个服务的时间戳都有几毫秒到几十毫秒的偏差请求在服务之间传递时也没有一个全局唯一的标识全靠订单号去各个日志文件里grep。更麻烦的是那个慢请求并不是每次都会出现偶发性能问题在日志里往往表现为查不到异常但就是慢。我把四个服务的日志拉到一起对着时间戳画时间线画到第二页纸的时候才猜到了可能是第三方渠道超时。事后我就在想如果有一套工具能自动把一次请求跨服务的调用关系画出来把每个环节的耗时标出来这个排查时间能压缩到原来的十分之一。这就是链路跟踪要解决的核心问题。1.2 Skywalking的技术定位无侵入的APM系统链路跟踪的概念大家都不陌生一次请求从入口开始经过网关、各个微服务、数据库、消息队列每一步都记录下来形成一个完整的调用链路。但实现方式差别很大。有些方案需要业务代码里手动埋点在关键方法上加注解、在调用处传上下文侵入性很强老项目改造起来简直是噩梦。Skywalking走的是另一条路Java Agent技术利用字节码增强在JVM加载类的时候动态改写字节码自动拦截HTTP请求、数据库访问、消息队列等常见组件的调用不需要修改一行业务代码。对SpringBoot项目来说这个优势非常关键。业务代码零改动只需要在启动JVM时加一个-javaagent参数应用就像什么都没发生一样继续跑但Skywalking已经在后台把所有关键调用链路记录下来了。对于已经上线、不方便大改的老项目这是最友好的接入方式。除了链路追踪Skywalking还包含指标监控、拓扑分析、告警等功能定位上属于完整的APM系统不只是链路跟踪这一个点。1.3 对比Zipkin和Pinpoint选型结论我在选型时重点看了三个开源方案Zipkin、Pinpoint、Skywalking。简单对比一下各自的差异。对比项ZipkinPinpointSkywalking接入方式HTTP/消息中间件上报需配合Brave等埋点Java Agent字节码增强Java Agent字节码增强代码侵入性需要引入依赖并配置埋点无侵入无侵入UI能力偏链路查询功能单一拓扑图、调用链、状态面板丰富拓扑图、调用链、指标、告警、性能剖析都有存储依赖MySQL/ES/CassandraHBaseH2/ES/MySQL等学习成本低中中社区活跃度一般一般活跃国内使用广泛Zipkin胜在轻量但功能边界比较窄基本就是一个Trace查询工具Pinpoint功能很强Agent做的很细但依赖HBase存储运维成本不低而且有时候Agent对字节码的改动太激进出现过跟业务框架冲突的情况。Skywalking各方面比较均衡Agent插件机制灵活OAP支持多种存储UI开箱即用再加上中文文档和社区都比较完善所以最终选了它。2. 环境准备把Skywalking OAP与UI先跑起来2.1 版本到底选8.x还是9.x第一次安装Skywalking的人最容易忽略一个点OAP服务端和Agent的版本兼容性。直接说结论SkyWalking 8.xOAP要求JDK 8对老环境友好。SkyWalking 9.xOAP要求JDK 11UI和OAP的部署方式有一些调整。如果你生产环境的SpringBoot应用是JDK 8不用担心Agent侧Agent本身是JDK 8编译的支持在JDK 8~17的JVM上运行。需要关注的是OAP服务端所在机器的JDK版本。如果你手头只有JDK 8的机器就老老实实用8.x的Agent和OAP如果能提供JDK 11直接用9.x也是顺理成章的选择。我的建议是Agent和OAP尽量用同一个版本至少大版本保持一致。跨大版本连接时gRPC接口的兼容性虽然官方会尽量保证但谁也不想在排查问题的时候先排查监控系统本身所以安装时直接从官网下载同一个发行包Agent就用包里的那个agent目录。2.2 解压后的目录每个角色负责什么下载发行包解压后目录结构是这样的apache-skywalking-apm-bin/ ├── agent/ # Java Agent用于挂载到SpringBoot应用 ├── bin/ # OAP和UI的启动脚本 ├── config/ # OAP的配置文件application.yml、alarm-settings.yml ├── oap-libs/ # OAP服务端的依赖库 └── webapp/ # UI前端应用Agent、OAP、UI三个角色各管一摊Agent部署在业务应用里负责采集数据OAP Server负责接收Agent上报的数据、分析计算并写入存储UI负责把数据可视化供人查看。发行包里这几个组件都齐了一套包就能把环境搭起来这也是Skywalking上手快的原因之一。2.3 启动OAP默认H2存储与切换ElasticsearchOAP的配置在config/application.yml里核心两部分端口和存储。如果是本地调试或者小型项目OAP默认用H2存储开箱即用不用装任何数据库。启动前先看一下端口配置core: default: gRPCPort: 11800 # Agent上报数据的gRPC端口 httpPort: 12800 # UI和后端查询接口使用的HTTP端口这两个端口要记住11800是给Agent上报用的12800是给UI查询用的。生产环境建议把存储切换到Elasticsearch改动配置里的存储选择器storage: selector: ${SW_STORAGE:elasticsearch} elasticsearch: clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200} namespace: ${SW_NAMESPACE:}切换存储的原因很现实H2是嵌入式数据库不适合保存大量历史监控数据而且查询性能在多服务长链路场景下会明显下滑。Elasticsearch本身就是为检索和分析而生的监控数据持续采集、按时间范围聚合查询的场景跟它非常匹配。小团队一开始用H2跑POC完全没问题但在准备让Skywalking承担真正的生产可观测性之前尽早切到ES是更稳妥的路线。2.4 启动UI并验证两个端口在bin目录下执行启动脚本可以同时启动OAP和UIbin/startup.sh也可以分开启动bin/oapService.sh bin/webappService.shUI默认端口是8080配置文件在webapp目录下不同版本文件名可能是application.yml或webapp.yml。如果UI和OAP不在同一台机器需要把UI配置里的OAP地址指向真正的OAP服务器默认是http://localhost:12800。启动完成后浏览器访问http://localhost:8080就能看到Skywalking的首页。此时系统里还没有任何业务数据页面是空的这正常。先用下面的命令确认服务都活过来了netstat -tlnp | grep -E 11800|12800|8080看到这三个端口都在监听说明OAP的gRPC、OAP的HTTP、UI三个服务都已经就绪可以开始接应用了。3. SpringBoot挂载Skywalking Agent的三种姿势3.1 IDEA本地调试VM options里加一行就够开发调试阶段就想看链路的话用IDEA跑SpringBoot最方便。打开Run/Debug Configurations找到你的SpringBoot启动类在VM options里加一行-javaagent:/path/to/apache-skywalking-apm-bin/agent/skywalking-agent.jar -Dskywalking.agent.service_nameorder-service -Dskywalking.collector.backend_service127.0.0.1:11800几个参数说明一下-javaagent指定Agent入口jar的路径注意要指向agent目录下的skywalking-agent.jar不能只复制这一个jar到别处因为Agent的插件和配置都在agent目录里它们要靠这个路径定位。-Dskywalking.agent.service_name是服务名会显示在Skywalking的拓扑图和Trace列表里建议跟SpringBoot应用名保持一致。-Dskywalking.collector.backend_service是OAP的地址和gRPC端口默认值是127.0.0.1:11800本地调试不用改但如果OAP在远程机器上这里改成OAP服务器IP:11800。配置好后正常启动SpringBoot应用的日志和业务功能不会受任何影响。这时候去Skywalking UI的服务列表里刷新一下应该能看到你刚定义的服务名出现了。3.2 生产部署JVM启动参数挂载全流程生产环境本质上跟IDEA是一样的只是把参数写到启动脚本里。假设Agent目录放在/opt/skywalking/agentSpringBoot应用包是order-service.jar启动命令这样写java -javaagent:/opt/skywalking/agent/skywalking-agent.jar \ -Dskywalking.agent.service_nameorder-service \ -Dskywalking.collector.backend_service10.0.0.10:11800 \ -Xms512m -Xmx512m \ -jar order-service.jar这里有两个我自己踩过坑后的习惯服务名一定不要带随机后缀比如order-service-01同一个服务的多个实例应该共用同一个服务名。Skywalking的拓扑图是按服务名聚合的如果你给每个实例起了不同的名字拓扑图会变成一团乱麻。backend_service不要配多个地址除非你明确知道自己在做负载均衡否则配多个地址反而可能让Agent在选择节点时出现奇怪的报错。按实际部署的OAP地址配一个就好。3.3 Docker容器部署挂载目录而不是打进镜像现在很多SpringBoot应用都在Docker里跑Agent的挂载思路要调整为挂载目录而不是把Agent打包进镜像。我的做法是把Agent目录放在宿主机上启动容器时挂载进去再用环境变量把参数传给JVMdocker run -d \ --name order-service \ -v /opt/skywalking/agent:/opt/skywalking/agent:ro \ -e JAVA_OPTS-javaagent:/opt/skywalking/agent/skywalking-agent.jar -Dskywalking.agent.service_nameorder-service -Dskywalking.collector.backend_service10.0.0.10:11800 \ -p 8080:8080 \ my-registry/order-service:1.0.0不过要注意这个方案要求你的镜像启动命令能识别JAVA_OPTS环境变量具体看Dockerfile里ENTRYPOINT怎么写的。如果用的是官方openjdk镜像直接java -jar那就需要把JAVA_OPTS手动拼进启动命令。还有一点挂载Agent目录时用:ro只读模式避免Agent在容器里写日志时产生奇怪的文件权限问题。如果你的基础设施是Kubernetes思路一样把Agent目录放到一个共享存储或者直接打进sidecar镜像里通过YAML里的command参数追加-javaagent。重点是保持一致Agent和OAP版本一致服务名规范一致上报地址可达。3.4 如何确认Agent已经生效接完Agent后别急着看UI先在应用侧确认Agent加载成功。SpringBoot启动日志里会有一段Agent输出的内容类似SkyWalking agent started successfully.同时Agent的工作目录下会有logs目录里面生成了skywalking-agent.log这个日志记录了Agent加载了哪些插件、连接OAP的状态。如果Agent启动失败或者连不上OAP先看这个文件。然后是UI侧。等几十秒后去Skywalking界面左侧菜单点服务列表应该能看到你配置的服务名出现了。刚接入时服务列表可能只有一个节点、没有拓扑连线这是正常的等有业务请求进来后调用关系才会慢慢生成。4. 看懂Dashboard服务拓扑与链路明细实战解读4.1 拓扑图服务之间的地图一次接口调用从网关进来经过订单、库存、用户等多个服务Skywalking会把它们画成一张拓扑图。图中的每个圆圈代表一个服务节点连线和箭头代表调用方向线的颜色和状态代表着健康度。比如正常流量是绿色出现错误率上升会变红一眼扫过去哪里有问题就一目了然。我第一次看拓扑图时有个直觉性的误解以为它是实时动态的。实际上它展示的是选定时间范围内的聚合关系。假设你选了最近15分钟那图上展示的就是这15分钟内所有服务间的调用关系、平均响应时间、吞吐量。看问题的时候先选对时间范围再根据颜色异常的节点往下钻取效率会高很多。4.2 Trace明细一次请求的完整拆解拓扑图帮你看哪个服务有问题Trace列表则帮你看这次请求到底经历了什么。在追踪菜单里每一行就是一条请求记录关键信息包括服务名、接口路径、总耗时、状态码、开始时间。点进某一条Trace你会看到这次请求被拆成一段段Span展开后就像看一个微型的时间轴网关收到HTTP请求网关调用订单服务订单服务查Redis订单服务调库存服务每个Span都有开始时间、结束时间、耗时、操作类型还能看到具体的SQL语句、HTTP URL、方法名。有时候别人问我你们那个接口为什么慢我已经养成了一个习惯不猜直接打开Skywalking按TraceId搜一下几秒钟就能看到瓶颈在哪个Span上。这里有个很实用的技巧在Trace列表页看耗时分布如果大部分Span都很快只有一个Span耗时特别长那基本就是这个环节的问题。如果所有Span都慢更可能是网络层的问题或者某个共享资源数据库连接池、线程池被占满了。不同类型的慢排查思路完全不一样。4.3 定位慢SQL和慢调用的实战思路Skywalking对数据库调用有内置插件MySQL、PostgreSQL、Redis这些常见组件都能自动识别。在Trace明细里数据库访问会单独作为一个Span显示里面有执行时间还能看到SQL语句。我实际排查过这样一个案例一个列表接口在压测时偶发超时接口本身逻辑很简单就是一次数据库查询加一次Redis缓存。从Trace里看到Redis读取有时能到800ms这就很反常了因为单次Redis读取正常情况都是零点几毫秒。顺着这个线索查下去发现是Redis连接池在部分场景下被慢查询占满了请求都堆在池子里等连接。如果当时没有Skywalking这种问题靠猜可能要猜很久。还有一点值得注意Skywalking的数据库Span里记录的SQL是Agent自动捕获的包含参数值时可能有敏感信息。生产环境建议关注一下Agent的插件配置必要时关闭SQL参数采集避免敏感数据落到监控存储里。5. 接入过程中的坑从排查思路到解决方案5.1 版本不匹配与数据空白的排查链路我见过最多的问题就是Agent配好了SpringBoot也启动正常但UI上就是什么都没有。我的排查顺序是这样的先看Agent日志skywalking-agent.log确认Agent是否成功启动、是否成功连接到OAP的gRPC端口。如果日志里有Connection refused说明Agent到OAP的网络不通检查collector.backend_service地址和防火墙。再确认端口。11800是gRPC端口Agent上报用的是它不是12800。有同事把backend_service配成了12800看起来端口是通的但Agent用gRPC协议连HTTP端口根本注册不上。然后看OAP侧日志。logs/skywalking-oap-server.log里如果有报错一般会直接指出问题比如存储初始化失败、端口被占用。最后看版本。Agent和OAP版本差太多比如Agent是8.6OAP是9.2gRPC的接口定义变化可能导致Agent的数据上报被拒绝或解析失败。虽然日志里不一定有明显报错但数据就是上不去。最稳妥的做法是同一个发行包里的Agent和OAP配套使用。这条链路走完95%的数据空白问题都能定位。5.2 端口通信链路11800和12800分别干什么很多新手对Skywalking的端口职责含糊我再说透一点。11800OAP的gRPC端口Agent采集到的数据走这个端口上报。12800OAP的HTTP端口UI查询数据走这个端口也开放给外部API调用。8080Skywalking UI的页面端口。正常部署时业务应用所在机器只要能访问OAP的11800端口即可UI所在机器只要能访问OAP的12800即可。安全组和防火墙的规则按这个逻辑来开不要图省事把三个端口全放给所有机器。之前有一次我们自己搭测试环境OAP和UI不在同一台机器UI白屏打不开数据排查了半天发现是UI机器访问不到OAP的12800端口在云安全组里加上规则就好了。5.3 异步线程链路断裂Async场景的修复这个问题在SpringBoot项目里非常典型主线程的链路是完整的但一进入异步线程Skywalking的Trace就断了拓扑图上表现为一个服务调用了另一个服务但调用链没有延续TraceId也对不上。原因不复杂链路上下文默认存在ThreadLocal里新线程不会继承主线程的上下文。项目里只要用了Async、自建线程池、或者CompletableFuture这类异步手段链路就可能在那个边界断开。解决办法有两个方向。官方toolkit包里提供了包装好的线程池实现ExecutorService executorService new TracingThreadPoolExecutor( 5, 10, 1000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100));把原来的new ThreadPoolExecutor替换成TracingThreadPoolExecutor任务提交时就会自动传递链路上下文集成成本非常低。如果是Spring的Async可以在自定义的TaskExecutor里用这个包装。另一个方案是手动做上下文传递在提交任务前用ContextManager.capture()抓取快照在任务执行开头用ContextManager.continued(snapshot)恢复。这个方案灵活但侵入性稍强适合无法替换线程池类型的场景。我自己的经验是与其到处缝缝补补不如在代码规范阶段就定好规则统一使用一个被TracingThreadPoolExecutor包装的线程池Bean避免每个开发各自new线程池导致漏处理。5.4 健康检查接口刷屏用trace-ignore插件过滤SpringBoot项目基本都会配健康检查、接口探活/actuator/health这类路径每几秒就被探一次如果它们进了SkywalkingTrace列表里会被大量健康检查请求占满真正需要关注的业务请求反而被淹没了。Skywalking有一个trace-ignore插件专门干这个。默认插件不在启用列表需要手动激活cp agent/optional-plugins/apm-trace-ignore-plugin-*.jar agent/plugins/然后在agent/config目录下新建apm-trace-ignore-plugin.config配置你想忽略的路径trace.ignore_path/actuator/health,/actuator/info,/actuator/metrics重启应用后这些路径的请求就不会上报到OAP了。别小看这个配置尤其在高频探活场景下它能帮你省掉大量无用数据的存储和展示开销。6. 工程化落地采样率、日志关联与告警6.1 采样率配置从全量采到按比例采Skywalking默认是100%采集所有链路。流量小的项目没问题但一旦QPS比较高全量采样对存储和OAP性能的压力会迅速增长。这跟业务系统做日志采集是同一个道理监控数据也要控制成本。Agent的采样率在agent/config/agent.config里配置agent.sample_ratio${SW_AGENT_SAMPLE_RATE:1}这里sample_ratio的值不是百分比而是分母。默认为1表示每1条Trace采1条即全量采样改成1000表示每1000条采1条也就是千分之一采样率。生产环境可以根据业务量和监控需求来权衡一般的经验是核心链路和排查高频问题的服务可以适当提高采样率非核心服务用千分之一甚至万分之一都够。不过要注意采样率是Agent本地决定的OAP端无法干预。调整配置后必须重启应用才能生效。6.2 日志与TraceId关联问题定位的最后一步链路跟踪有一个很关键的配套能力让业务日志带上TraceId。有了它排查问题时的流程就变成Skywalking里找到慢请求的TraceId然后去日志系统里过滤这个TraceId把这次请求的所有日志全捞出来。没有这一步链路跟踪的价值会打折扣。实现方式很简单引入官方toolkit依赖dependency groupIdorg.apache.skywalking/groupId artifactIdapm-toolkit-logback-1.x/artifactId version与Agent版本保持一致/version /dependency然后在logback的pattern里用%tid输出TraceIdencoder classch.qos.logback.core.encoder.LayoutWrappingEncoder layout classorg.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{39} - %msg%n/pattern /layout /encoder新版Agent本身也会尝试把TraceId写入MDCkey是tid所以有些项目直接用%X{tid}也能拿到。我建议固定用toolkit方式它对版本的兼容性更稳定。配置完成后重启应用随便打一条日志看效果你会看到每行日志前面都带着一长串TraceId跟Skywalking里的是同一个值。6.3 告警规则让Skywalking主动发现问题链路跟踪不只是被动的排查工具它还能主动发现问题。Skywalking OAP内置了告警能力配置文件在config/alarm-settings.yml。默认的告警规则里包含一些实用的维度比如端点平均响应时间超过阈值、服务错误率升高、数据库慢调用等。生产环境一般会根据自己的业务特点调整阈值。举个例子把响应时间超过1秒的接口在3次采样中持续超时定义为需要告警rules: endpoint_avg_response_time: metrics-name: endpoint_avg_response_time threshold: 1000 op: period: 10 count: 3 message: Endpoint {name} response time over 1000ms in recent 2 minutes告警消息发到哪里Skywalking支持配置Webhook地址把告警推给内部的消息平台或群机器人webhooks: - http://你的告警接收服务地址/notify收到告警后再去Skywalking里查对应的链路就能快速确定哪个服务、哪个端点、什么时间段出了问题。这套组合拳下来慢接口的发现从用户投诉后被动排查变成了系统主动通知你体验完全不一样。我自己实际用下来的感受是Skywalking不是装上去就完事的工具它真正的价值在于持续使用过程中形成的排查习惯——遇到性能问题先看拓扑再看Trace最后用TraceId回捞日志三步下来基本能把问题范围缩小到很具体的环节。如果你的SpringBoot项目还在靠人工翻日志定位问题真心建议花半天时间把Skywalking搭起来这个时间投入会从第一次实战排查开始回本。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →