SkyWalking实战应用

一、为什么金融项目首选 SkyWalking 而不是 Zipkin?

我们选型时对比了 SkyWalking 和 Zipkin,最终选 SkyWalking 三个核心原因:

第一,零侵入探针。 金融项目大量老系统,代码改造成本高、风险大。SkyWalking 的 Java Agent 通过字节码插桩自动埋点,我们12个核心服务一周内全部接完,业务代码零改动。Zipkin 需要手动埋点或依赖 OpenTelemetry,接入成本高得多。

第二,多语言统一视图。 我们系统有 Java 主服务、Go 的结算服务、Python 的风控脚本。SkyWalking 一个界面看全链路,Zipkin 各语言各自一套,体验割裂。

第三,内置告警开箱即用。 金融项目对稳定性要求高,SkyWalking 自带告警引擎,配好规则直接对接钉钉/短信。Zipkin 本身没有告警能力,需要自己搭 AlertManager,运维成本翻倍。

对比维度 SkyWalking Zipkin
语言无关性 多语言支持(Java/Go/Node/Python),UI 统一 主要 Java,其他语言需各语言 SDK
探针方式 Java Agent 零侵入,字节码插桩自动埋点 需手动埋点或 OpenTelemetry 介入
告警能力 内置告警规则引擎,开箱即用 依赖 AlertManager 等第三方
UI 功能 拓扑图/链路/实例/日志联动,一站式 链路为主,其他能力弱

二、OAP 和 Java Agent 到底是什么?

OAP 不是消息队列

OAP = Observability Analysis Platform(可观测性分析平台),是 SkyWalking 的核心服务进程,负责:

  • 接收 Agent 采集的数据(gRPC/HTTP)
  • 聚合计算,生成拓扑图和调用链
  • 触发告警规则
  • 协调写入存储层(ES/MySQL)

很多人搞混的一个点:OAP 不是 Kafka 或 RabbitMQ。 Kafka/RabbitMQ 是 OAP 集群模式下用于多实例间同步数据的中间件,是可选组件,不是 OAP 本身。

Java Agent 是什么

Java Agent 是 JVM 的外挂插件,通过 -javaagent 参数加载,通过字节码插桩 在业务代码运行过程中动态织入监控逻辑 ,业务代码零改动

工作原理一句话:用 ByteBuddy 在类加载前/时修改字节码,在方法入口出口自动插入埋点代码。

三个核心职责:

  1. 字节码插桩 --- 扫描需要监控的类(HTTP、MySQL、Dubbo 等),在方法入口/出口自动插入埋点代码

  2. 数据采集 --- 采集调用耗时、SQL 内容、异常堆栈、traceId 等

  3. 数据上报 --- 通过 gRPC 异步推送给 OAP Server

    java -javaagent:/opt/skywalking/agent/skywalking-agent.jar
    -Dsw.agent.service_name=payment-service
    -jar app.jar

一个 -javaagent 参数就搞定了,业务代码一行不用改。


三、SkyWalking 核心架构

复制代码
┌─────────────────────────────────────────────┐
│              SkyWalking UI                  │
│   (拓扑图 / 调用链 / 告警 / JVM 监控)        │
└──────────────────┬──────────────────────────┘
                   │ REST / GraphQL
┌──────────────────▼──────────────────────────┐
│              OAP Server                     │
│  ┌─────────┐   ┌─────────┐   ┌──────────┐  │
│  │ Reciever│ → │Aggregator│ → │  Sender │  │
│  │ (接收)  │   │ (聚合)   │   │ (存储)   │  │
│  └─────────┘   └─────────┘   └──────────┘  │
└──────────────────┬──────────────────────────┘
                   │ gRPC
┌──────────────────▼──────────────────────────┐
│           Java Agent (Sidecar)             │
│  ByteBuddy 字节码插桩 → 自动埋点            │
│  HTTP / Dubbo / MySQL / Redis / MQ         │
└─────────────────────────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│         Storage (Elasticsearch/MySQL)      │
└─────────────────────────────────────────────┘

架构分四层:

  • Java Agent:字节码插桩,零代码侵入
  • OAP Server:数据聚合、拓扑图计算、告警触发
  • Storage:链路数据持久化,生产用 ES 支撑日均亿级数据
  • UI:调用链详情、拓扑图、告警展示三合一

核心是 Agent 和 OAP 通过 gRPC 异步通信,不阻塞业务线程。


四、Spring Boot 项目接入 SkyWalking 完整步骤

Step 1:下载 Agent

复制代码
wget https://archive.apache.org/dist/skywalking/9.7.0/apache-skywalking-apm-9.7.0.tar.gz
tar -xzf apache-skywalking-apm-9.7.0.tar.gz -C /opt/skywalking/

Step 2:配置 agent.config(三项关键配置)

复制代码
# 服务名(显示在 UI 拓扑中)
agent.service_name=${SW_AGENT_NAME:payment-service}

# OAP Server 地址(gRPC 端口)
collector.backend_service=${SW_AGENT_COLLECTOR:192.168.1.100:11800}

# 采样率(高并发控制存储)
agent.sample_n_per_3_secs=10

Step 3:启动时挂载

复制代码
java -javaagent=/opt/skywalking/agent/skywalking-agent.jar \
     -Dsw.agent.service_name=payment-service \
     -Dsw.collector.backend_service=192.168.1.100:11800 \
     -Dsw.agent.sample_n_per_3_secs=10 \
     -jar payment-service.jar

Step 4:Docker 环境接入

复制代码
ENTRYPOINT ["java","-javaagent","/agent/skywalking-agent.jar",\
            "-Dsw.agent.service_name=payment-service",\
            "-Dsw.collector.backend_service=oap:11800",\
            "-jar","/app/payment-service.jar"]

Step 5:OAP + ES + UI 一键部署

复制代码
# docker-compose.yml
version: '3.8'
services:
  elasticsearch:
    image: elasticsearch:7.17.0
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms2g -Xmx2g
    ports:
      - "9200:9200"

  oap:
    image: apache/skywalking-oap-server:9.7.0
    environment:
      SW_STORAGE_ES_CONNECTOR_SERVERADDRESSES: elasticsearch:9200
    ports:
      - "11800:11800"  # gRPC:Agent 数据上传
      - "12800:12800"  # REST:UI 查询接口

  ui:
    image: apache/skywalking-ui:9.7.0
    environment:
      BACKEND_SERVICE: oap:12800
    ports:
      - "8080:8080"

接入就三步:下载 Agent → 配置服务名和 OAP 地址 → 启动时加 -javaagent 参数。业务代码完全不用改。


五、OAP 高可用:为什么必须做?怎么做?

单实例 OAP 挂了会怎样?

很多人忽视这个,但 OAP 单实例是生产隐患:

时间 后果
< 30 秒 Agent 本地缓冲,短暂数据积压
> 30 秒 缓冲文件写满,Agent 报 Buffer is full, data will be dropped链路数据永久丢失
> 5 分钟 告警失效,监控盲区

OAP 挂了 30 秒就开始丢数据,而链路数据是金融项目排查事故的唯一证据,丢了等于盲测。

为什么用 Nginx 而不是 Spring Cloud Gateway 或 SLB 做负载均衡?

这是被问得最多的追问。

核心原因:SkyWalking Agent 与 OAP 之间走的是 gRPC 协议 。Spring Cloud Gateway 和公有云 SLB 不原生支持 gRPC 原始 TCP 转发,而 Nginx 通过 stream 模块做 TCP 层透明代理,Agent 完全无感知。

对比 Nginx Spring Cloud Gateway 公有云 SLB
gRPC 支持 ngx_stream_proxy 原生支持 不支持 不支持
Agent 兼容性 完全透明 需要改造 Agent 需要改造 Agent
性能 C 语言,高并发低内存 JVM 进程,资源占用大 云厂商控制

完整高可用架构图

Copy

复制代码
                                    ┌──────────────────┐
                                    │   SkyWalking UI  │
                                    │   (Port 8080)    │
                                    └────────┬─────────┘
                                             │ HTTP/GraphQL
                           ┌─────────────────┴──────────────────┐
                           │          Nginx (Master + Backup)   │
                           │   upstream oap_http {              │
                           │     server oap-1:12800;           │
                           │     server oap-2:12800;           │
                           │     server oap-3:12800;            │
                           │   }                                │
                           │   stream {                         │
                           │     upstream oap_grpc {            │
                           │       server oap-1:11800;          │
                           │       server oap-2:11800;          │
                           │       server oap-3:11800;          │
                           │     }                              │
                           │   }                                │
                           │   keepalived VIP: 11800/12800      │
                           └─────────────────┬──────────────────┘
                                             │ gRPC / HTTP
                    ┌────────────────────────┼────────────────────────┐
                    │                        │                        │
            ┌───────▼───────┐        ┌───────▼───────┐        ┌───────▼───────┐
            │   OAP Server  │        │   OAP Server  │        │   OAP Server  │
            │    (Node-1)   │        │    (Node-2)   │        │    (Node-3)   │
            └───────┬───────┘        └───────┬───────┘        └───────┬───────┘
                    │                        │                        │
                    └────────────────────────┼────────────────────────┘
                                             │ Storage Cluster
                                    ┌────────▼────────┐
                                    │  Elasticsearch  │
                                    │   (3节点集群)    │
                                    └─────────────────┘

  Agent 端配置:collector.backend_service=VIP:11800(Keepalived 虚拟IP)

Agent 端多地址故障转移:

Copy

复制代码
# 方式一:Nginx VIP(推荐,透明)
collector.backend_service=192.168.1.200:11800

# 方式二:直连多个 OAP
collector.backend_service=oap-1:11800,oap-2:11800,oap-3:11800

六、真实生产案例

案例 1:MySQL 慢查询定位

场景: 账务服务响应时间从 200ms 飙到 2s,对账延迟告警。

排查过程:

  1. SkyWalking Trace 列表按耗时排序
  2. 找到一笔 2100ms 的账务调用,点进详情
  3. 调用链:/account/debitMySQL: UPDATE account ... WHERE user_id = ? [1980ms]
  4. 根因:历史数据迁移后 ANALYZE TABLE 统计信息过期,MySQL 选错执行计划,全表扫描
  5. 解决:ANALYZE TABLE account; + 加组合索引(user_id + status)

效果: 没有 SkyWalking 之前要登录多台机器 grep 日志拼 traceId,半天打底。有了 SkyWalking,5分钟定位到是哪台 MySQL、哪条 SQL。分析表后恢复到 50ms。


案例 2:跨服务调用链追踪

场景: 支付成功率骤降,支付→账务→银行网关三方调用定位。

SkyWalking 调用链:

复制代码
总耗时:1.8s
payment-service: /pay        1.8s
└── account-service: debit   1.5s
    └── bank-gateway: POST   1.2s  ← 银行接口超时
        └── MySQL: INSERT    80ms

结论: 银行网关抖动,账务服务无责,触发熔断降级。

那次支付成功率从 99.5% 掉到 95%,打开 SkyWalking 拓扑图,5 分钟看到是银行网关超时。立刻触发熔断返回"银行维护中",同时告警通知银行接口负责人。如果靠日志排查,估计要查 2 小时。


案例 3:自定义告警 + WebHook 钉钉

alarm-settings.yml:

复制代码
rules:
  - name: endpoint_resp_time
    metrics-name: endpoint_resp_time
    threshold: 3000
    op: ">"
    duration: 1
    count: 2
    message: "服务 {name} 接口 {operation} 响应时间超过 3 秒"

  - name: error_rate
    metrics-name: service_error_rate
    threshold: 0.05
    op: ">"
    duration: 1
    count: 3
    message: "服务 {name} 错误率超过 5%"

  - name: mysql_slow
    metrics-name: database_access_resp_time
    data-name: mysql
    threshold: 1000
    op: ">"
    duration: 1
    count: 2
    message: "MySQL {data-name} 执行超过 1 秒"

webhooks:
  - http://alert-gateway:8080/alert/callback

告警回调(Spring Boot):

复制代码
@RestController
@RequestMapping("/alert")
public class AlertController {

    @PostMapping("/callback")
    public void onAlert(@RequestBody AlertDTO alert) {
        for (Alarm alarm : alert.getAlarmList()) {
            if (alarm.getScope() == AlarmLevel.CRITICAL) {
                // 严重告警:电话 + 短信双通道(金融合规要求)
                phoneService.callOnDuty();
                smsService.sendCritical(alarm.getMessage());
            } else {
                // 普通告警:钉钉机器人
                dingTalkClient.send(webHookUrl, buildMsg(alarm));
            }
        }
    }
}

案例 4:OAP 高可用故障切换

背景: 3 个 OAP 实例 + Nginx + Keepalived VIP,19 个微服务接入,日均 1.2 亿链路数据。

故障过程:

复制代码
00:05  OAP-2 OOM,进程退出
00:05  Nginx 检测到 oap-2:11800 连接失败,自动转发到 oap-1/oap-3
00:05  Agent 侧无感知,数据正常上报
00:30  运维完成 OAP-2 重启,少量数据积压
01:00  全部恢复正常,ES 数据完整

关键指标:

指标 数据
OAP 故障到恢复 < 5 分钟
链路数据丢失 < 0.01%
业务影响 零影响

七、性能影响与采样控制

面试时一定要能回答这个问题:Agent 对业务性能影响有多大?怎么控制?

Agent 本身对业务性能损耗 <3%,主要来自字节码插桩和数据发送。生产通过三件事控制开销:

第一,采样控制。 高并发服务设 agent.sample_n_per_3_secs=10,每秒只完整追踪 3-4 个请求,其余只记录基础指标,省 70% 存储。

第二,异步发送。 Agent 数据先放内存缓冲,异步推给 OAP,不阻塞业务线程。

第三,接入前压测验证。 JMeter 对比有无 Agent 的 TP99,差值 <3% 才上生产。