简介:面向微服务架构转型团队的APM全链路监控方案PPT,聚焦应用性能管理在分布式环境下的落地难点,适合运维、研发与架构师快速建立监控体系认知。内容系统梳理了APM从网络监控到云计算微服务时代的演进脉络,重点剖析微服务带来的依赖关系复杂、持续交付、容器化环境与注册发现等挑战,并给出分布式追踪、服务注册发现、容器监控、智能分析等平台构建能力。还结合Google Dapper与OpenTracing规范、Java探针的字节码增强原理,讲解了端到端链路追踪和代码级问题定位方法,以及监控、告警、报障的闭环设计。包体为1个pptx文件,压缩包大小1.09MB,结构紧凑,图文结合便于直接用于技术分享汇报。已有278人学习下载,适合作为方案汇报、团队内训或APM选型参考。
1. 微服务APM全链路监控在解决什么问题
一套十几个微服务的系统同时在线,某个接口突然从 200ms 变成 2s,反馈链路是:前端说网关慢,网关说订单服务慢,订单服务说用户服务超时,用户服务说缓存连接池满了。每个服务都有日志、有监控、有告警,但每样东西都是孤岛,串不起来。APM 全链路监控要解决的就是这件事:用同一个 Trace ID 把一次请求从网关到数据库的所有调用串成一条完整链路,顺带把微服务架构图里的真实调用关系、每个环节耗时、数据库慢 SQL、异常堆栈全部自动画出来。主流方案通过字节码增强做无侵入采集,业务代码改动量很小,适合正在被跨服务排查折磨的开发和运维,也适合压测团队用来定位高并发下的真实瓶颈。
2. 微服务APM链路追踪的建模逻辑与监控工具选型
2.1 为什么单体时代不需要全链路监控
单体应用的一次请求,所有调用栈都发生在同一个进程里,一次 thread dump、一把 Arthas 就能把耗时分布看明白。微服务拆分之后,一次用户操作要跨多个节点、多个进程、多台机器,甚至跨多个消息队列,调用方和被调用方各自记录的时间戳如果不对齐,连先调了谁、后调了谁都没法确认。全链路监控本质上是给分布式系统建立一套统一的“请求身份证”机制,让散落在各服务日志里的记录能够按同一个 ID 重新聚合。
这套机制的核心是三个概念:Trace、Span、Baggage。Trace 是一次请求的完整路径,包含多个 Span;Span 是路径上的一个调用单元,比如一次 HTTP 调用、一次数据库查询、一次消息发送;Baggage 是随请求传递的业务上下文,用来把用户 ID、订单号这类信息带到下游服务。理解这三个概念,后面配置采样率和排查断链问题就顺了。
2.2 Trace、Span 与调用链的基本数据结构
探针采集到的数据落到存储里之前,本质上就是一张张 Span 表。一个 Span 至少要记录以下字段,这也是选型和排错时最常看的维度:
| 字段 | 说明 | 实际取值示例 |
|---|---|---|
| traceId | 整条链路的全局唯一 ID | a1b2c3d4e5f6... |
| spanId | 当前 Span 的唯一 ID | span-001 |
| parentSpanId | 父 Span 的 ID,跨服务时靠它串联 | span-000 |
| serviceName | 所属微服务名 | ruoyi-order |
| operationName | 本次操作名,如 URL 或接口方法 | POST /order/create |
| startTime / endTime | 开始与结束时间 | 1720000000000 / 1720000000123 |
| tags | 关键属性,如 HTTP 方法、状态码、SQL 语句 | http.method=POST |
| logs | 该 Span 内的异常或关键事件 | error.stack=xxx |
2.2.1 从两个 Span 看懂链路怎么串起来
假设请求先到网关,再转给订单服务。网关侧生成 Span A,随后通过 HTTP 头把 traceId 和新生成的 SpanId 传给订单服务;订单服务接收后,会创建一个 parentSpanId 指向网关那个 Span 的新 Span B。两个 Span 的 traceId 相同,parent-child 关系通过 parentSpanId 建立。UI 上的微服务架构图和调用链,就是从这些 Span 里还原出来的。
2.3 微服务APM监控工具有哪些:四类方案对比
市面上的 APM 工具基本可以分成四类:SkyWalking、Zipkin、Jaeger、Pinpoint。它们最大的差异在埋点方式、存储依赖和 UI 完整度上。
| 工具 | 接入方式 | 存储后端 | 核心优势 | 常见短板 |
|---|---|---|---|---|
| SkyWalking | Java Agent 字节码增强 | ES、BanyanDB、MySQL | 拓扑自动生成,UI 完整度高,探针插件全 | 高并发下存储成本需要规划 |
| Zipkin | 客户端埋点或 Sleuth 集成 | ES、Cassandra、内存 | 轻量,和 Spring Cloud 生态贴合 | 只有链路查询,拓扑和告警弱 |
| Jaeger | SDK 或 Envoy 集成 | ES、Cassandra、Kafka | 云原生场景支持好 | 服务端指标、告警能力偏弱 |
| Pinpoint | Java Agent | HBase | 调用栈细节非常细 | 运维成本高,升级比较重 |
微服务项目第一次落地 APM,优先考虑 SkyWalking。原因是它对 Java 技术栈的插件覆盖最全,Spring Cloud、Dubbo、gRPC、RocketMQ、Kafka 都有现成插件,UI 里能直接看到服务拓扑、端点耗时、数据库语句和 JVM 指标。Zipkin 更适合已经用了 Spring Cloud Sleuth 的老项目,Jaeger 更适合云原生和链路查询为主的场景。
2.4 埋点方式怎么选:Agent、SDK 还是日志
APM 的埋点方式直接决定改造成本。第一种是 Agent 方式,通过-javaagent参数启动应用,在字节码层面拦截 HTTP、RPC、数据库等调用,业务代码一行不改,是当下微服务接入的默认选择。第二种是 SDK 方式,代码里显式调用工具包创建 Span,适合自研框架或者 Agent 覆盖不到的私有协议。第三种是纯日志方式,各服务按约定把 traceId 打进日志,再由采集端聚合,成本最低但还原不了完整调用树。
新项目或者改造存量微服务项目,先用 Agent 方式跑通,只有遇到 Agent 插件覆盖不到的场景再补 SDK 手动埋点。判断标准很简单:看目标服务用的是不是主流框架,如果是,Agent 基本够用。需要注意探针版本和 JDK 版本要匹配,SkyWalking 8.x 对 JDK 8 支持最好,JDK 17 及以上建议直接用 9.x。
3. 用 SkyWalking 在本地搭一套微服务APM全链路监控环境
3.1 用 Docker Compose 起一个最小可用的监控环境
搭建阶段不建议一次性上生产集群,先在单机把 OAP 服务端、存储、UI 三件套跑起来,验证链路数据能正常上报,再考虑扩容。常见的做法是用 Docker Compose 编排,存储先用 Elasticsearch 7.x,因为 9.x 的 SkyWalking 对 ES 7 的兼容性最稳定,后面要换 BanyanDB 也只需要改环境变量。
version: '3.8' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.17.10 container_name: sw-es environment: - discovery.type=single-node - ES_JAVA_OPTS=-Xms1g -Xmx1g ports: - "9200:9200" networks: - sw-net oap: image: apache/skywalking-oap-server:9.7.0 container_name: sw-oap depends_on: - elasticsearch environment: - SW_STORAGE=elasticsearch - SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200 - SW_CORE_DEFAULT_ROLE=Mixed ports: - "11800:11800" - "12800:12800" networks: - sw-net ui: image: apache/skywalking-ui:9.7.0 container_name: sw-ui depends_on: - oap ports: - "8080:8080" environment: - SW_OAP_ADDRESS=http://oap:12800 networks: - sw-net networks: sw-net: driver: bridge11800是探针上报链路数据的 gRPC 端口,12800是 UI 和 OAP 通信的 HTTP 端口,SW_STORAGE_ES_CLUSTER_NODES指向 Elasticsearch 的地址,多个节点用逗号分隔。启动后访问http://部署服务器IP:8080就能看到 SkyWalking UI,此时没有服务接入,拓扑图是空的。
3.2 将微服务接入APM:以若依微服务整套环境为例
若依微服务的各个子服务是标准的 Spring Cloud 应用,接入方式完全一致,不需要改 pom.xml,不需要改 Java 代码。先把 SkyWalking Agent 探针包下载解压到每个服务所在服务器的固定目录,例如/opt/skywalking-agent,目录里必须有skywalking-agent.jar和config/agent.config。
启动命令修改为:
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \ -Dskywalking.agent.service_name=ruoyi-system \ -Dskywalking.collector.backend_service=10.0.1.20:11800 \ -Xms512m -Xmx512m \ -jar ruoyi-system.jar-javaagent参数必须放在-jar之前,JVM 才会在加载主类前启用探针。skywalking.agent.service_name是在 UI 里显示的服务名,同一个服务的多个实例要用同一个名字,否则拓扑图里会出现两个孤立节点。skywalking.collector.backend_service指向 OAP 的 gRPC 地址,注意是11800而不是 UI 的8080。
生产环境服务多的时候,不推荐把这三个参数手写进启动脚本。K8s 环境可以把 Agent 路径挂到 PVC,再通过环境变量JAVA_TOOL_OPTIONS=-javaagent:/opt/skywalking-agent/skywalking-agent.jar注入,避免改动每个 Deployment 的启动命令。
3.3 验证监控数据:从微服务架构图确认接入成功
接入后随便触发几个接口,让调用关系产生真实数据。可以先用 curl 简单地压一下:
curl -s -o /dev/null -w "耗时: %{time_total}s\n" \ http://localhost:8080/ruoyi-system/user/list然后打开 SkyWalking UI,进入“拓扑图”页面。正常情况下几分钟内就能看到服务节点以及节点间的连线,连线上标注着平均耗时和调用量。这张微服务架构图是探针自动生成的,比人工画的架构图可靠,因为它反映的是真实运行时的调用关系。
UI 里还需要看两个地方:“服务”页面里每个服务的实例列表、JVM 指标是否正常;“追踪”页面里按 traceId 搜索,能查到一条完整的链路,包含各 Span 的耗时和状态。如果“追踪”里查不到数据,先检查 OAP 的 11800 端口从应用服务器能否连通,再检查agent.service_name是否重复。
4. 微服务APM落地后避不开的三个问题:采样率、异步链路、日志关联
4.1 探针侧采样率怎么配置才不会把存储打爆
全链路监控接入简单,但一把所有服务全量采集,ES 的索引增长会快得吓人。压测环境一次 10 分钟高并发,可能产生几千万条 Span,磁盘和查询性能都扛不住。SkyWalking 探针侧支持按固定频率采样,配置项是agent.config里的agent.sample_n_per_3_secs。
| 配置值 | 含义 | 适用场景 |
|---|---|---|
-1 | 全部采样 | 压测阶段、核心交易链路 |
0 | 完全不采样 | 临时调试关闭链路 |
3 | 每 3 秒采集 3 条 | 默认值,适合日常运行 |
生产环境推荐从默认值开始观察,压测期间临时改为-1全采。配置生效不需要重启服务,可以通过 JVM 参数覆盖:-Dskywalking.agent.sample_n_per_3_secs=-1。注意这个值是每 3 秒的采样条数,不是百分比,高并发场景下3已经能满足大部分问题定位需求。
4.2 消息队列和 @Async 异步线程的链路为什么会断
基于 HTTP 的同步调用,探针通过 HTTP Header 传递 traceId 和 parentSpanId,链路天然是通的。但微服务里大量使用消息队列和异步线程,这是链路断掉的重灾区。RocketMQ、Kafka 这类主流 MQ,SkyWalking 插件会自动把链路上下文放到消息头里,消费者侧自动续上。但业务自己创建的线程池、Spring@Async异步方法,默认情况下上下文不会自动传递。
判断链路是否断了有个简单方法:在 UI 的追踪详情里,如果看到某个 Span 的parentSpanId为空,而它上游明明还有调用,说明上下文在异步边界丢掉了。常见做法是用官方 toolkit 提供的@Trace注解配合crossThread属性,让异步任务继承父线程的上下文,业务代码里只需要在方法上补注解,不需要手写传递代码。
4.2.1 给自定义线程池加一个链路开关
使用@Async时,可以在@Configuration类里自定义ThreadPoolTaskExecutor,把一个装饰器挂到任务的Runnable上,在提交线程任务时捕获当前链路上下文,让下游拿到完整 traceId。这块代码各个项目差异大,建议先在测试环境做一次真实链路验证,确认异步环节的 traceId 和父请求一致后,再推广到全部线程池。
4.3 把链路 TraceID 写进业务日志,排查问题不再两头翻
APM 链路数据和业务日志通常是两套系统,定位问题时先在 APM 里找到 traceId,再去日志平台搜,中间要手工复制粘贴。更顺滑的做法是让业务日志直接带上当前 traceId,这样从日志平台搜到一条报错,马上能反查它在整条链路中的位置。SkyWalking 官方提供了 logback 的 toolkit 扩展,不需要手写 MDC 赋值。
在logback-spring.xml里把输出格式改成:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender>里面的%tid占位符在使用 SkyWalking 探针启动应用后,会被自动替换成当前请求的 traceId。前提是引入官方提供的 logback 1.x toolkit 依赖,并保持 Agent 探针版本和 toolkit 版本同属一个大版本。改造完成后,同一请求在多个微服务产生的日志里都能看到同一个 traceId,日志检索从“按时间猜”变成“按 ID 定点捞”。
5. 用 APM 数据验证微服务链路改造效果的检查清单
5.1 压测前必看的 APM 基线指标
按标题这个场景,如果要做高并发压测验证云上环境承载能力,压测前应该先让 APM 跑出健康基线。我会在压测脚本启动之前,打开 UI 的“服务”页面,按顺序确认三件事:各服务实例数是否符合预期,JVM 堆内存和 GC 时间是否稳定,数据库面板里当前慢 SQL 数量是不是零。基线没跑稳就开始压测,后面所有结论都可能被环境噪音干扰。
JMeter 压测开始后,不要只看聚合报告里的 TPS 和响应时间,APM 的价值在于把数据分层。压测过程中同时切到 SkyWalking 的“端点”页面,按 P99 耗时排序,哪个接口最慢会直接浮出来;再点进该端点看它的下游 Span,是下游服务本身慢,还是数据库查询慢,还是连接池等待时间长,一眼就能分开。压测结束后把 JMeter 的 P99 和 APM 的 P99 做一次对账,偏差超过 20% 优先检查压测机到网关之间的链路瓶颈,而不是应用本身。
5.2 一次压测瓶颈定位的标准动作
压测发现问题时,我一般按这套动作处理:先在“追踪”页面按耗时倒序拉出最慢的若干条链路,抄下 traceId;去日志平台按 traceId 搜索对应服务日志,确认应用层有没有异常堆栈;如果链路显示数据库 Span 耗时占比超过 60%,直接到数据库面板定位慢 SQL。这个方法能覆盖大多数性能问题,关键步骤是压测期间 APM 的采样率必须调成-1,否则最慢的链路可能恰好被采样机制漏掉。
最后补一个日常习惯:每周固定看一眼拓扑图,重点观察有没有出现新节点、新连线或者调用量异常突增的依赖。微服务架构在没有人工干预的情况下发生了变化,这本身就是一条值得追查的告警。
本文还有配套的精品资源,点击获取