Skip to content

Apache SkyWalking:从遥测接收到查询的 OAP 架构解析

SkyWalking 面向微服务和云原生系统,把探针采集的链路、指标、日志、事件与性能剖析数据汇入 OAP(Observability Analysis Platform)后端。OAP 既是协议入口,也是分析与聚合运行时:接收器把不同格式翻译为统一领域对象,分析流水线计算指标和拓扑,可插拔存储承接写入与查询,Horizon UI、Grafana 或 API 客户端再从查询面读取结果。

SkyWalking 架构的关键不在“支持多少协议”,而在模块化内核怎样让接收、分析、存储和查询彼此解耦。本文固定到源码提交 25c74a8,重点阅读 OAP 的启动装配、原生追踪接收、OAL/MAL 分析、存储接口和查询路径;各语言 Agent、Rover、BanyanDB 与 Horizon UI 均是独立仓库,正文只说明外部项目与 OAP 的边界,不把外部项目的实现算入本次覆盖。

GitHub 仓库信息

信息内容
项目标题Apache SkyWalking
项目描述面向云原生分布式系统的开源可观测性平台,提供遥测数据采集、分析、聚合、查询与可视化能力。
GitHub 仓库apache/skywalking
官网地址skywalking.apache.org
主要开发语言Java 96.94%、Shell 1.86%、ANTLR 0.43%
开源许可证Apache-2.0
最近代码更新2026-09-12(GitHub pushed_at,核对日期:2026-09-13)

四个角色,一条数据主线

官方架构把系统分成 Probe、Platform backend、Storage 和 UI 四部分。Probe 可以是语言 Agent、服务网格、eBPF Rover,也可以是 OpenTelemetry、Zipkin、Prometheus 等生态组件。当前仓库主要实现 Platform backend,也就是 OAP;存储和 UI 通过明确接口连接,具体产品可以独立演进。

Mermaid 流程图
查看源码
flowchart LR
  P[探针与开放协议] --> R[Receiver 接收器]
  R --> A[Analyzer 分析与聚合]
  A --> W[流式 Worker]
  W --> S[(Storage 接口)]
  S --> B[(BanyanDB / Elasticsearch / JDBC)]
  Q[GraphQL / PromQL / TraceQL / LogQL] --> S
  U[Horizon UI / Grafana / API 客户端] --> Q
  C[配置、集群、告警、导出] -.协作.-> A

这里有一个容易误解的边界:SkyWalking 不是把原始遥测简单转存后再交给前端计算。OAP 在写入前完成语义建模、聚合、拓扑关系生成与规则计算,因此存储插件面对的是 OAP 定义的数据模型和 DAO 契约,而不是任意协议的原始报文。

仓库结构与职责

text
skywalking/
├── apm-protocol/                 # OAP 使用的网络协议与生成代码
├── oap-server/
│   ├── server-starter/           # 进程入口与配置加载
│   ├── server-library/           # 模块系统、网络等基础库
│   ├── server-core/              # 领域模型、分析、存储和查询契约
│   ├── server-receiver-plugin/   # SkyWalking、OTel、Zipkin 等接收器
│   ├── analyzer/                 # trace、meter、log、event 等分析器
│   ├── server-storage-plugin/    # BanyanDB、Elasticsearch、JDBC 实现
│   ├── server-query-plugin/      # GraphQL、PromQL、TraceQL、LogQL
│   ├── server-cluster-plugin/    # 集群发现与节点协作
│   ├── server-alarm-plugin/      # 告警规则和通知
│   └── server-telemetry/         # OAP 自身遥测
├── dist-material/                # 发布包脚本和默认配置
├── docker/                       # OAP + 存储 + Horizon UI Compose
└── docs/                         # 架构、构建、部署与扩展文档

pom.xml 默认组合 apm-protocoloap-server、BOM 与发行包;oap-server/pom.xml 又列出接收、分析、存储、查询、集群、告警和遥测等子模块。这比目录数量更重要:OAP 的扩展边界由 Maven 模块与运行时模块系统共同维持。

想理解的能力首要入口向下阅读
启动与配置server-starterlibrary-module 的模块生命周期
Trace 接入skywalking-trace-receiver-pluginagent-analyzer 与 core analysis
指标规则oal-grammaroal-rt生成的 metrics 代码与 worker
多生态指标otel-receiver-pluginmeter-analyzer 的 MAL 脚本
数据落盘server-core/storageBanyanDB、Elasticsearch、JDBC provider
查询与 UIquery-graphql-plugincore query service 与 Storage DAO

模块生命周期:OAP 的真正骨架

Java 入口 OAPServerStartUp.main 只调用 OAPServerBootstrap.start()。后者读取运行模式和应用配置,再把配置交给 ModuleManagerModuleManager 使用 Java ServiceLoader 发现 ModuleDefineModuleProvider,按配置为每个模块选中 provider,随后建立依赖有向图并启动。

Mermaid 流程图
查看源码
sequenceDiagram
  participant Main as OAPServerStartUp
  participant Boot as OAPServerBootstrap
  participant Config as ApplicationConfigLoader
  participant MM as ModuleManager
  participant Flow as BootstrapFlow
  participant Provider as ModuleProvider
  Main->>Boot: start()
  Boot->>Config: load()
  Config-->>Boot: ApplicationConfiguration
  Boot->>MM: init(configuration)
  MM->>MM: ServiceLoader 发现模块与 provider
  MM->>Provider: prepare()
  MM->>Flow: 按 requiredModules 建立启动顺序
  Flow->>Provider: start()
  Flow->>Provider: notifyAfterCompleted()
  Flow->>Provider: notifyBootCompleted()
  Boot->>Boot: 标记 ServerStatus 已启动

ModuleProvider 的阶段划分有实际约束:prepare 处理不依赖其他模块的初始化,start 才允许模块互操作,全部模块完成通知后才进入 notifyBootCompleted。源码注释明确说明核心网络端口在最后阶段开放,避免请求进入一个仍在启动的模块。provider 同时声明 requiredModules(),错误选择、缺失模块或循环依赖会在启动期失败,而不是等到第一条遥测到达才暴露。

这套设计的收益是同一个逻辑模块可以有多个实现,例如 Storage 选择 BanyanDB、Elasticsearch 或 JDBC。代价是启动行为同时由配置、SPI 注册和依赖图决定;排查“模块不存在”时只看 YAML 不够,还要核对发行包是否包含对应 provider。

从一条 Trace 到可查询结果

不同协议的入口类并不相同,但主链可以稳定地拆成四段:协议接收、语义分析、流式聚合、存储写入。下表把文章覆盖的代表性链路和重要分支放在一起。

链路入口核心交接输出或副作用关键分支
OAP 启动OAPServerStartUp配置 → ModuleManager → BootstrapFlow模块就绪并开放端口provider 缺失、依赖环、init 模式
原生 TracegRPC Trace receiversegment → trace analyzer → source/worker指标、拓扑、链路记录采样、过滤、跨节点转发
OTel/MeterOTel receiver协议转换 → MAL/meter analyzer统一指标模型规则匹配、未知指标、标签维度
日志log receiverLAL 解析 → 日志分析/采样日志记录与派生指标解析失败、丢弃、trace 关联
查询GraphQL/PromQL 等query service → Storage DAO面板或 API 响应存储能力、时间范围、表达式错误

接收与分析不是同一层

Receiver 负责协议和传输边界,例如原生 Trace、OpenTelemetry、Zipkin、Envoy、Zabbix 或 Telegraf。分析器负责 SkyWalking 语义:把数据归入 Layer、Service、Service Instance、Endpoint 和 Process 等实体,并触发指标与关系计算。这样新增协议通常不必复制完整分析逻辑;反过来,修改领域模型会影响多个接收器和查询面,需要更谨慎地评估兼容性。

原生 Trace 的源码交接可以从接收器取得的 ISegmentParserService 开始。接收器把 gRPC 报文还原为 SegmentObject 后调用 SegmentParserServiceImpl.send;该方法为本次 segment 创建 TraceAnalyzer 并进入 doAnalysisTraceAnalyzer 依次驱动已注册 listener:入口、出口和 RPC listener 生成服务与端点关系,segment listener 形成 Trace 记录,TraceSegmentSampler 决定是否保留完整 segment。各 listener 最终把 core source 交给 SourceReceiver,再由对应 stream processor 进入聚合或持久化。这样,采样可以减少完整 Trace 存储,却不会被误解为简单地关闭所有服务指标。

OAL(Observability Analysis Language)用于声明服务、实例、端点等维度上的聚合指标;MAL(Meter Analysis Language)让 Prometheus、OpenTelemetry、Telegraf 等指标走脚本化的统一处理。ANTLR grammar、运行时和生成代码共同把规则变成执行逻辑。规则带来可配置性,也把一部分错误从 Java 编译期移动到规则装载和运行期,调试时应先确认规则是否成功加载,再追踪 worker 与存储。

MAL 的代表性链路更直接:接收器形成同一次采集的 SampleFamily 集合,规则对应的 Analyzer 在构建阶段用 ANTLR4 与 Javassist 编译表达式、解析所需样本和标签,并向 MeterSystem 注册指标;运行阶段只选取表达式引用的样本,应用可选标签过滤,执行表达式,再把结果交给 MeterSystem 存储。运行时规则更新采用“先全部编译、全部成功后再注册”的两阶段处理,避免同一规则文件后半段失败时留下部分 schema。

日志链由 LAL(Log Analysis Language)控制。规则执行产生 LALOutputBuilder 后,RecordSinkListener 先用日志 metadata 补齐服务、Layer、时间和 Trace 上下文,再调用 builder 的 complete(SourceReceiver)。默认 builder 输出 Log,自定义 builder 还能输出 Envoy access log、数据库慢语句或采样 Trace;规则没有产生有效 builder 时,listener 直接返回,不会制造空记录。

写入与查询通过契约相遇

分析产生的指标、关系、日志和 Trace 由 server-core/storage 定义的存储接口承接,具体 provider 实现索引、批写和查询 DAO。查询插件并不直接了解 Elasticsearch 或 BanyanDB 的客户端细节,而是从 StorageModule 取得对应查询服务。query-graphql-plugin 把 schema 和 resolver 接到 core query service,service 再向当前 Storage provider 索取 DAO;GraphQL 是 Horizon UI 的主要公共查询面,PromQL、TraceQL、LogQL 和 Zipkin API 则服务兼容工具和已有生态。查询失败应按“请求语法 → query service 参数 → DAO 能力 → 存储状态”的顺序定位。

这也是第二个重要转折:更换存储并非只替换连接字符串。接口屏蔽了客户端实现,但索引模型、保留周期、查询能力、迁移与容量规划仍由具体后端决定。生产选型必须同时看查询语义和运维约束。

集群、失败与扩展边界

OAP 可以同时承担 Receiver 与 Aggregator,也可以拆分角色。Receiver 节点接收数据后,需要根据服务或实体键把分析任务路由到适当节点;集群 provider 负责成员发现,remote 模块承接跨节点传输。默认架构不强制引入消息队列,减少组件数量,但峰值削峰、跨区域网络和故障隔离需要由部署架构另行解决。

最值得关注的失败点并不只在入口:

  • 配置选择了发行包中不存在的 provider,模块准备阶段即失败;循环依赖由启动图检测。
  • 存储不可用会影响批写和查询,健康检查只能说明 OAP 端口可连接,不能证明后端已能正常读写。
  • 接收成功不等于数据一定出现在面板;采样、过滤、规则不匹配、实体时间窗口和存储延迟都可能改变结果。
  • 查询语言插件共享底层数据,但语义并不完全等价;迁移仪表盘前应核对支持的函数、标签和时间范围。

扩展 OAP 能力时,优先判断新能力属于哪一层:新遥测格式进入 receiver;新聚合进入 OAL/MAL 或 analyzer;新持久化后端实现 Storage provider 与 DAO;新查询协议进入 query plugin。跨过这些边界直接调用具体 provider,短期容易,长期会破坏替换能力。

获取源码、构建与调试

官方构建说明要求 Git、Maven 3.6+ 和 JDK 11/17/21/25,并强调仓库含 Git submodule,不建议直接拿 GitHub 自动生成的源码压缩包进行编译。以下命令来自固定提交的官方文档,本次没有执行:

bash
git clone --recurse-submodules https://github.com/apache/skywalking.git
cd skywalking
git checkout 25c74a80860dc09d6281526345650ceee46dcf37
./mvnw clean package -Dmaven.test.skip

成功后发行包应出现在 dist/。只构建后端和发行包可使用 ./mvnw package -Pbackend,dist。IDE 首次导入前,官方建议先执行 ./mvnw compile -Dmaven.test.skip=true,因为 gRPC、Protobuf、FlatBuffers 与 ANTLR 会生成源码;没有生成目录时,大量“类不存在”通常是构建准备不完整,而非业务源码损坏。

调试 OAP 启动可按以下顺序缩小范围:

  1. OAPServerBootstrap 确认配置文件是否成功解析。
  2. ModuleManager.prepare 查看模块与 provider 是否被 SPI 发现。
  3. 根据 requiredModules() 检查依赖图,而不是任意调整模块顺序。
  4. 端口开放后再发送测试遥测,并同时检查 OAP 自身遥测和存储日志。

Docker Compose 最短体验

固定提交的 docker/docker-compose.yml 是官方维护路径,提供 elasticsearchbanyandb 两个 profile,均由 OAP、存储和 Horizon UI 组成。下面使用更贴近 SkyWalking 当前生态的 BanyanDB 路径;docker/.env 中的镜像 tag 应固定到与目标发布匹配的版本,不能把示例的 latest 直接用于生产。

在仓库的 docker/ 目录运行:

bash
docker compose --profile banyandb config
docker compose --profile banyandb up -d
docker compose --profile banyandb ps

官方配置把遥测入口映射到 localhost:11800,OAP 查询端口为 12800,管理端口为 17128,Horizon UI 在 http://localhost:8080,默认登录信息来自只读挂载的 horizon.yaml。首次启动 BanyanDB 并安装 schema 时,OAP 可能需要 60–120 秒;Compose 的健康检查为此设置了启动宽限期。

这条 Compose 路径只完成基础设施启动验收:先确认存储与 OAP 均为 healthy,再打开 Horizon UI 并确认登录页可访问。产生真实遥测还需要选择与应用语言相符的官方 Agent,并把后端地址设为 127.0.0.1:11800;Agent 的安装命令与版本由对应语言仓库维护,不在本文拼接一个可能过期的示例。完成 Agent 接入后,以 Horizon UI 出现预期服务、实例和 Trace 作为业务成功信号。重建 OAP 容器后数据是否仍在,取决于外部存储的持久化配置。仓库演示 Compose 的 BanyanDB 命令把数据写在容器 /tmp 路径且未声明持久卷,因此这份 Compose 适合本地预览,不构成生产持久化方案。

停止演示可用 docker compose --profile banyandb down。不要在需要保留数据的部署中盲目追加 -v;生产环境还需独立设计存储备份、版本兼容、认证、TLS 和网络暴露。本文只完成了官方文件与配置的静态核对,没有运行 Compose、拉取镜像或验证恢复与升级。

仓库内的 Agent Skills

.claude/skills/ 下有 9 个以贡献任务命名的 Skill 文件:compilegenerate-classestestpackagerun-e2eci-e2e-debuglicensegh-pull-requestnew-monitoring-feature。目录位置和名称说明这些文件属于仓库内部维护入口,而不是 OAP 运行时组件;本次没有采集 Skill 正文,因此不进一步推断触发条件、命令或执行结果。

OAP 的 Maven 模块和 Compose 均未把 .claude/skills 纳入运行路径。本文不评价这些 Skill 的运行效果或跨 Agent 兼容性。

设计取舍与适用场景

维度SkyWalking 的选择带来的收益需要承担的成本
数据模型以服务、实例、端点、进程和 Layer 建模APM 拓扑与故障定位更直接接入协议要映射到统一语义
扩展方式模块 + provider + ServiceLoader存储、集群和接收实现可替换配置、SPI 与依赖图增加排障层次
分析时机OAP 流式分析后写入查询侧负担更可控规则错误会影响后续数据,需要治理变更
生态兼容原生协议并接入 OTel、Prometheus、Zipkin 等可复用既有采集体系各查询/标签语义不能假定完全一致
默认拓扑不强制消息队列组件更少、入门部署更短大规模削峰与故障隔离需额外设计

SkyWalking 适合希望把 APM 拓扑、分布式追踪、多来源指标与日志关联放在同一平台,并愿意运营 OAP 与外部存储的团队。只需要采集和转发遥测时,OpenTelemetry Collector 更轻;以指标与 PromQL 为中心时,Prometheus/Grafana 通常更直接;以日志或搜索为中心时,Elastic Stack 的数据模型更自然。选择依据应是主要排障问题和运维能力,不是协议清单长度。

常见问题

构建时出现大量生成类缺失:先确认 submodule 完整,再执行官方的 Maven compile。Protobuf、gRPC、FlatBuffers 和 ANTLR 输出未生成时,IDE 索引无法代表项目真实可编译状态。

OAP 启动时报 module/provider missing:核对 application.yml 选择项和发行包内容,检查 provider 的 SPI 注册及 requiredModules()。不要用复制 JAR 的方式掩盖版本不一致。

容器 healthy,但 UI 没有数据:端口探针只证明进程可连接。继续检查 Agent 的服务名与后端地址、接收器是否启用、OAP 是否完成 schema 初始化、存储是否可写,以及数据是否落在查询时间范围。

更换存储后历史数据不见了:Storage provider 可替换不等于数据自动迁移。切换前应按目标后端的官方迁移与备份方案处理索引、schema 和保留策略,并用服务列表、指标和 Trace 查询做业务验收。

后续源码阅读路径

要新增协议,从对应 receiver provider 的 prepare/start 与 handler 开始,追到 core source 和 analyzer;要新增指标,先读 OAL/MAL grammar、运行时和生成代码,再找存储模型;要排查面板查询,从 GraphQL schema 或兼容查询插件进入 query service,最后落到 Storage DAO。修改模块装配或生命周期前,应先读 ModuleManagerBootstrapFlowModuleProvider,因为一次看似局部的启动调整可能改变全部 provider 的就绪顺序。

总结

OAP 的骨架是带依赖顺序的模块生命周期,数据主线是 receiver 将协议对象交给 analyzer,再由 source、worker 和 Storage provider 完成聚合与持久化,查询插件沿 core query service 和 DAO 读回结果。模块与 provider 让协议、存储和查询实现可以替换,同时也把配置、SPI 注册、规则编译和存储 schema 变成必须共同治理的运行条件。

本次源码抽样已经覆盖启动、Trace、MAL、LAL、存储和查询边界,但没有逐个分析所有 receiver、存储 provider 与集群实现,也没有执行构建或容器。读者可以按实际目标选择下一步:数据不入库时沿 Trace 或 meter 链向下查,查询异常时从 query plugin 反向追到 DAO,开发新扩展时先守住 receiver、analyzer、storage 与 query 的模块边界。

参考资料

全部公开文章由同一个站点构建和发布。