下一篇【第58篇】Spring Async探针插件开发——ContextSnapshot跨线程追踪的终极解决方案上一篇【第60篇】探针注册通信扩展——基于HTTP的SPI注册实现完全指南一、开篇 —— “为什么只用gRPC”如果你参加过SkyWalking的线上答疑这个问题一定能排进被问最多的十个问题前三名“为什么SkyWalking Agent上报数据只能用gRPC明明HTTP更通用啊”“Kafka不好吗为什么你们不支持我直接用Kafka上报”“我们公司网关只放行HTTP流量gRPC用不了怎么办”这些问题背后有一个共同的潜台词“你们是不是有gRPC情结”作为一个在APM领域摸爬滚打多年的系统SkyWalking当然不是gRPC脑残粉。今天这篇文章我们就来扒一扒这背后的设计决策。我会尽量用轻松的口吻但信息量绝对不打折。二、通信选型的决策树在SkyWalking的架构中通信具体指的是什么我们先画个图看清楚------------------------------------------------------------------ | SkyWalking通信层全景 | ------------------------------------------------------------------ | | | ┌─────────────────┐ ┌─────────────────┐ | | │ Java Agent │ │ Go Agent │ | | │ (.NET Agent) │ │ (Python) │ | | │ (Node Agent) │ │ (Rust) │ | | └────────┬────────┘ └────────┬────────┘ | | │ │ | | │ ① 注册通信 (Register) │ | | │ ② 数据上报 (Trace/Metrics/Log) | | │ │ | | └───────────┬───────────────┘ | | │ | | ↓ | | ┌───────────────────────┐ | | │ OAP Server │ | | │ ┌─────────────────┐ │ | | │ │ gRPC接收层 │ │ ← 官方默认 | | │ │ 端口: 11800 │ │ | | │ └─────────────────┘ │ | | │ │ | | │ ┌─────────────────┐ │ | | │ │ HTTP接收层 │ │ ← 可选扩展 | | │ │ 端口: 12800 │ │ | | │ └─────────────────┘ │ | | │ │ | | │ ┌─────────────────┐ │ | | │ │ Kafka消费者 │ │ ← 可选扩展 | | │ └─────────────────┘ │ | | └───────────────────────┘ | | | ------------------------------------------------------------------三、官方选gRPC的十大理由不只有五个3.1 理由一性能 —— “天下武功唯快不破”在APM场景下通信性能是硬指标。你得想象一下一个中等规模的微服务系统Agent每秒可能产生几千条Trace数据。如果通信层慢100ms那就是几百个请求的延迟。gRPC的优势------------------------------------------------------------------ | gRPC vs HTTP/JSON 性能对比示意数据 | ------------------------------------------------------------------ | | | 指标 gRPC/Protobuf HTTP/JSON | | ─────────────────────────────────────────────────────────────── │ | 序列化速度 快二进制 慢文本解析 │ | 消息体积 小10-30% 大JSON有冗余字段 │ | 连接复用 天然支持HTTP/2 需要额外配置 | | 多路复用 是 否HTTP/1.1 | | 流量控制 内置 需自行实现 | | 双向流 支持 不支持HTTP/1.1 | | 长连接 天然 需要Keep-Alive | | | ------------------------------------------------------------------// Protobuf vs JSON 序列化对比// Protobuf消息约30字节messageTraceSegmentObject{string traceSegmentId1;repeatedSpanObjectspans2;}// 等价的JSON约150字节{traceSegmentId:a1b2...,spans:[{spanId:0,operationName:GET:/api,...}]}// 结论Protobuf比JSON小约80%序列化速度快3-5倍3.2 理由二双向流 —— “不只上报还要下发”SkyWalking的Agent和OAP之间不只是Agent单方面上报数据。还有这些场景需要OAP下发给Agent配置动态下发修改采样率、关闭某插件命令下发触发性能剖析Profiling心跳交互Agent状态汇报------------------------------------------------------------------ | gRPC双向流示意 | ------------------------------------------------------------------ | | | Agent OAP Server | | ──────── ────────── | | Register | | ─────────────────────────────→ (服务注册) | | | | InstancePing | | ←───────────────────────────── (配置/命令下发) | | | | TraceSegmentReport | | ─────────────────────────────→ (Trace上报) | | | | JVMMetricReport | | ─────────────────────────────→ (Metrics上报) | | | | ProfilingTaskQuery | | ←───────────────────────────── (Profiling命令) | | | | 如果是纯HTTP单向上报命令下发只能靠Agent定期轮询 | | gRPC的双向流让这个变成推模式实时性更好 | | | ------------------------------------------------------------------3.3 理由三生态统一 —— “不用每种语言实现一套序列化”SkyWalking支持Java/.NET/Go/Python/Node.js/PHP等多种语言的Agent。如果通信协议不一致每种语言都要实现一套序列化/反序列化。Protobuf天然跨语言——你定义一份.proto文件然后用protoc生成各语言的代码即可// TraceSegmentService.proto service TraceSegmentReportService { rpc collect (stream UpstreamSegment) returns (Commands) { } } message UpstreamSegment { int32 globalTraceIds 1; bytes segment 2; // TraceSegment } // 这份proto文件可以生成 // - Java: TraceSegmentReportServiceGrpc.java // - Go: trace_segment_service.pb.go // - Node: trace_segment_service_pb.js // - Python: trace_segment_service_pb2.py如果用自定义的HTTP协议每种语言的Agent都要自己实现一套HTTP客户端 JSON序列化 连接池 重试逻辑。这不是多做一套的问题而是做了N套的成本。3.4 理由四社区精力有限这是最现实但最少被提及的原因。SkyWalking是一个开源社区驱动的项目维护者是志愿者精力是有限的。每支持一种通信方式意味着代码实现Agent端 OAP端文档编写测试覆盖持续维护Bug修复 版本升级社区支持回答问题与其把精力分散到三四种通信方式上不如把一种做到极致。这是开源项目常见的少即是多策略。3.5 理由五历史惯性 渐进式决策SkyWalking 6.x开始引入gRPC到7.x全面切换。这个决策不是一步到位的而是逐步验证的结果SkyWalking通信史 5.x ──── HTTP REST API ──── 性能差不支持流 6.x ──── gRPC (实验) ──── 性能好但迁移成本高 7.x ──── gRPC (默认) ──── 全面切换HTTP作为可选 8.x ──── gRPC Kafka ──── 支持更多场景四、为什么不提供更多通信方式承认一个现实SkyWalking不是不想支持更多通信方式而是社区没需求到那个程度和维护资源不足以支撑。但是SkyWalking的架构设计让你可以自己扩展。官方提供了扩展点SPI机制让社区开发者可以添加自定义通信方式。Kafka Reporter、HTTP Reporter就是按照这个思路实现的。------------------------------------------------------------------ | SkyWalking通信扩展点 | ------------------------------------------------------------------ | | | SkyWalking架构中预留了这些扩展点 | | | | ① Agent端 | | - Reporter SPI (负责数据上报) | | - 内置gRPCReporter | | - 可选KafkaReporter, HttpReporter | | | | ② OAP端 | | - Receiver SPI (负责数据接收) | | - 内置GRPCReceiver | | - 可选KafkaFetcher, HTTPReceiver | | | | ③ 如果你有特殊需求 | | 实现Reporter接口 → 打包 → 放入optional-plugins → 搞定 | | | ------------------------------------------------------------------五、社区扩展的必要场景5.1 场景一网络受限环境------------------------------------------------------------------ | 典型的网络受限场景 | ------------------------------------------------------------------ | | | ┌─────────────────────────────────────────┐ | | │ 生产环境 A (VPC / 私有网络) │ | | │ ┌──────┐ ┌──────┐ ┌──────┐ │ | | │ │ App1 │ │ App2 │ │ App3 │ │ | | │ │Agent │ │Agent │ │Agent │ │ | | │ └──┬───┘ └──┬───┘ └──┬───┘ │ | | │ │ │ │ │ | | │ │ gRPC (端口 11800) │ | | │ │ ← 防火墙规则只放行80/443? │ | | │ │ gRPC过不去 │ | | │ │ │ | | │ └────────┬────────┘ │ | | └──────────────┼──────────────────────────┘ | | │ X (被防火墙拦截) | | ↓ | | ┌──────────────┼──────────────────────────┐ │ | │ 运维监控区 │ │ │ | │ ┌───────────▼──────────────┐ │ │ | │ │ OAP Server │ │ │ | │ │ 端口 11800 不通 │ │ │ | │ └──────────────────────────┘ │ │ | └─────────────────────────────────────────┘ | | | | 解决方案 | | - 使用HTTP扩展走80/443端口 | | - 使用Kafka作为中间缓冲消息队列通常有专用网络通道 | | | ------------------------------------------------------------------5.2 场景二大规模集群的数据缓冲当Agent实例数达到几千甚至上万时所有Agent直连OAP的gRPC端口会造成OAP成为连接热点网络波动时数据容易丢失# 使用Kafka作为缓冲的方案# Agent → Kafka → OAP# 优点解耦 缓冲 削峰5.3 场景三多云/混合云架构跨云网络延迟高、不稳定gRPC长连接可能频繁断开。使用Kafka或HTTP代理可以缓解。六、官方提供的HTTP扩展方案SkyWalking官方其实提供了HTTP通信扩展只是需要手动配置# agent/config/agent.config# 将上报通道从gRPC切换为HTTPagent.reporter.protocolhttp# 配置HTTP上报地址plugin.toolkit.http.collector_server_backend http://oap-server:12800/receive# 或者用Kafkaplugin.kafka.bootstrap_serverskafka1:9092,kafka2:9092plugin.kafka.topic_segmentskywalking-segments plugin.kafka.topic_metricsskywalking-metrics// HTTP Reporter 的核心实现逻辑简化publicclassHttpReporterimplementsReporter{privatefinalHttpClienthttpClient;privatefinalStringcollectorUrl;Overridepublicvoidreport(TraceSegmentsegment){// 序列化为JSON或Protobufbyte[]dataserialize(segment);// HTTP POST上报HttpPostpostnewHttpPost(collectorUrl);post.setEntity(newByteArrayEntity(data));post.setHeader(Content-Type,application/x-protobuf);httpClient.execute(post);}// 缺点HTTP每次都要建立/释放连接// 批量场景下性能不如gRPC的长连接}七、幽默总结如果你问SkyWalking的PMC“为什么不用HTTP”他们可能会回答用不那么官方的语气“我们不是不用HTTP我们只是把HTTP留在了需要它的人手里。就像你不会用自行车去送大件快递一样——gRPC是我们的卡车稳、快、能抗。但如果你家的路只让自行车过只能走80端口我们也有折叠自行车HTTP扩展给你。你还可以选择走铁路Kafka——虽然要多绕一圈但胜在稳定。”“我们做开源讲究的是——给你最多选择但默认给你最好的选择。”下一篇【第58篇】Spring Async探针插件开发——ContextSnapshot跨线程追踪的终极解决方案上一篇【第60篇】探针注册通信扩展——基于HTTP的SPI注册实现完全指南