尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【SkyWalking从入门到精通】第35篇:OAP Server模块化框架:像搭积木一样构建可观测平台

【SkyWalking从入门到精通】第35篇:OAP Server模块化框架:像搭积木一样构建可观测平台
📅 发布时间:2026/7/12 5:19:54

上一篇【第34篇】TraceId的生成算法——一个ID背后的精巧工程
下一篇【第36篇】OAP Server启动流程解析:从一行命令到一个可观测大脑的诞生


一、开篇:为什么OAP需要一个模块化框架?

想象一下你去一家自助餐厅。餐厅老板告诉你:今天有川菜师傅,所以有麻辣口味;明天川菜师傅休假,换粤菜师傅掌勺,口味就变成清淡的。更神奇的是,你甚至可以自己带一个意大利面师傅来,把某个档口变成意面专区——而且不需要重新装修厨房。

这就是SkyWalking OAP Server的模块化框架想要达到的效果。

OAP(Observability Analysis Platform)作为SkyWalking的大脑,需要同时处理多种任务:接收探针数据、分析指标、存储追踪数据、发送告警、管理集群等等。如果把这些功能全部硬编码在一起,维护成本会像滚雪球一样越滚越大。更麻烦的是,不同用户的部署环境千差万别——有人用Elasticsearch做存储,有人用MySQL;有人用ZooKeeper做集群协调,有人在Kubernetes上跑;有人需要对接企业微信告警,有人则用钉钉。

模块化框架的核心目标就是:让OAP像乐高积木一样,每个功能都是一块独立的积木,你可以自由替换、增减,而不会让整个城堡倒塌。

+-----------------------------------------------------------------------+ | OAP Server | | | | +------------+ +------------+ +------------+ +------------+ | | | 存储模块 | | 集群模块 | | 告警模块 | | 遥测模块 | | | | (ES/MySQL) | |(ZK/Nacos) | |(钉钉/企微) | | (Prometheus)| | | +------------+ +------------+ +------------+ +------------+ | | | | | | | | +------------+ +------------+ +------------+ +------------+ | | | 分析模块 | | 查询模块 | | 接收器模块 | | 配置模块 | | | +------------+ +------------+ +------------+ +------------+ | | | +-----------------------------------------------------------------------+ 图1:OAP Server就是一个模块拼图

二、核心概念:Module与Provider的双簧戏

模块化框架的设计围绕两个核心接口展开:ModuleDefine和ModuleProvider。理解它们的关系,是理解整个框架的钥匙。

2.1 ModuleDefine:模块的"身份证"

ModuleDefine是一个抽象类,每个模块都必须继承它。它定义了一个模块"是什么":

publicabstractclassModuleDefine{// 模块的唯一名称,比如 "storage"、"cluster"、"core"privatefinalStringname;// 模块提供的服务接口类型// 模块间通过Service接口进行依赖注入publicabstractClass<?extendsService>[]services();// 模块的实现选择器(如果有的话)// 为null表示普通模块,非null表示可选择实现的模块publicModuleConfigcreateConfigBeanIfAbsent(){returnnull;}// 当这个模块只有一个Provider时,是否必须要有ProviderpublicbooleansupportMultiImplement(){returnfalse;}}

每个ModuleDefine的本质就是一句话:“OAP有个叫XXX的能力槽位,它对外提供这些服务接口”。

2.2 ModuleProvider:模块的"具体实现"

如果ModuleDefine是"身份证",那ModuleProvider就是"持证上岗的人"。同一个模块可以有多个Provider,就像"保安岗位"可以由张三站岗,也可以由李四站岗,但同一时间只能有一个人站岗。

publicabstractclassModuleProvider{// 属于哪个模块privateModuleDefinemodule;// Provider的唯一名称privateStringname;// 依赖的其他模块服务privatefinalMap<Class<?extendsService>,Service>requiredServices;// 准备工作(读取配置、建表等)publicabstractvoidprepare()throwsModuleStartException;// 启动服务publicabstractvoidstart()throwsModuleStartException;// 所有模块启动完成后的回调publicabstractvoidnotifyAfterCompleted()throwsModuleStartException;// 注册自己的服务实现publicabstractServiceregisterServiceImplementation(Class<?extendsService>serviceType)throwsServiceNotProvidedException;}

2.3 关系图解

ModuleDefine (模块定义-身份证) | | 属于 v +---------+---------+ | | ModuleProvider A ModuleProvider B (ES存储实现) (MySQL存储实现) | +-------+-------+ | | Service A Service B (IStorageDAO) (IModelInstaller) 图2:ModuleDefine、ModuleProvider、Service三者关系

关键理解:

  • 一个ModuleDefine可以有多个ModuleProvider(多实现可选)
  • 一个ModuleProvider可以注册多个Service(对外暴露多个服务接口)
  • 模块之间通过requiredServices进行依赖注入

三、application.yaml中的模块配置之美

每个模块在application.yml中都有属于自己的配置区块。看一个典型的配置片段:

# application.yml - OAP Server模块配置core:selector:${SW_CORE:default}# 核心模块(通常就一个实现)default:# 核心模块的配置gRPCHost:${SW_CORE_GRPC_HOST:0.0.0.0}gRPCPort:${SW_CORE_GRPC_PORT:11800}restHost:${SW_CORE_REST_HOST:0.0.0.0}restPort:${SW_CORE_REST_PORT:12800}maxConcurrentCallsPerConnection:${SW_CORE_MAX_CONCURRENT:0}storage:# 存储模块selector:${SW_STORAGE:elasticsearch}# 选择器:决定用哪个存储实现elasticsearch:# Elasticsearch实现的配置nameSpace:${SW_NAMESPACE:""}clusterNodes:${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}protocol:${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}user:${SW_ES_USER:""}password:${SW_ES_PASSWORD:""}# ...更多ES配置h2:# H2实现的配置(默认用于Demo)driver:${SW_STORAGE_H2_DRIVER:org.h2.jdbcx.JdbcDataSource}url:${SW_STORAGE_H2_URL:jdbc:h2:mem:skywalking-oap-db}user:${SW_STORAGE_H2_USER:sa}mysql:# MySQL实现的配置# ...MySQL配置cluster:# 集群模块selector:${SW_CLUSTER:standalone}# 选择器:standalone/zk/nacos/k8sstandalone:# 单机模式-无需额外配置zookeeper:hostPort:${SW_CLUSTER_ZK_HOST_PORT:localhost:2181}sessionTimeout:${SW_CLUSTER_ZK_SESSION_TIMEOUT:40000}nacos:serviceName:${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}hostPort:${SW_CLUSTER_NACOS_HOST_PORT:localhost:8848}# ...Nacos配置

注意配置结构的美妙之处:

  1. selector字段决定使用哪个Provider实现
  2. 环境变量占位符(如${SW_STORAGE:elasticsearch})让配置可以在运行时切换
  3. 每个Provider的配置区块独立,互不干扰

四、模块依赖声明:谁依赖谁?

模块之间不是孤岛。存储模块需要知道"我存到哪里",核心模块需要存储模块来持久化数据。依赖关系通过ModuleProvider中的requiredModules来声明。

// 分析模块的实现类publicclassAnalyzerModuleProviderextendsModuleProvider{@OverridepublicModuleConfigcreateConfigBeanIfAbsent(){returnnewAnalyzerModuleConfig();}// 声明依赖关系@OverridepublicString[]requiredModules(){returnnewString[]{"core",// 依赖核心模块"storage",// 依赖存储模块"cluster"// 依赖集群模块};}@Overridepublicvoidprepare()throwsModuleStartException{// 从依赖模块获取服务this.getManager().find("storage").provider().getService(IStorageDAO.class);this.getManager().find("cluster").provider().getService(ClusterModuleClient.class);}}

依赖关系构成了一个有向无环图(DAG),框架在启动时会进行拓扑排序,确保被依赖的模块先启动。

+-----------+ | Core | | (核心) | +-----+-----+ | +----------+----------+ | | +-----v-----+ +----v------+ | Storage | | Cluster | | (存储) | | (集群) | +-----+-----+ +----+------+ | | +----------+----------+ | +------v------+ | Analyzer | | (分析引擎) | +------+------+ | +------v------+ | Alarm | | (告警) | +-------------+ 图3:模块依赖的有向无环图(DAG)

五、模块管理器的初始化全流程

一切的核心是ModuleManager。它就像一个交响乐团的指挥,负责:

  1. 扫描所有可用的"乐手"(模块)
  2. 确认每个乐手都能"演奏"(配置正确)
  3. 安排"排练顺序"(拓扑排序)
  4. 指挥"演奏"(三阶段启动)
publicclassModuleManager{// 所有已加载的ModuleDefineprivatefinalMap<String,ModuleDefine>loadedModules;// 模块配置(从application.yml解析而来)privatefinalApplicationConfigurationapplicationConfiguration;// 初始化publicvoidinit(){// 第一阶段:扫描模块loadModules();// ServiceLoader扫描所有的ModuleDefine// 第二阶段:解析配置loadConfigurations();// 解析application.yml,绑定到各模块配置// 第三阶段:加载ProviderloadProviders();// 根据selector选择正确的Provider// 第四阶段:拓扑排序sortProviders();// 按依赖关系排序// 第五阶段:三阶段启动prepareAllModules();// prepare阶段startAllModules();// start阶段notifyAfterCompleted();// 所有模块启动完成通知}}

初始化流程图:

+-----------------+ | OAPServerBootstrap| | 启动入口 | +--------+---------+ | v +--------+---------+ | ModuleManager | | .init() | +--------+---------+ | +--------------+--------------+ | | v v +---------+----------+ +---------+----------+ | ServiceLoader扫描 | | 解析application.yml | | 所有ModuleDefine | | 构建配置对象 | +---------+----------+ +---------+----------+ | | +--------------+--------------+ | v +--------------+--------------+ | 根据selector为每个模块 | | 选择对应的ModuleProvider | +--------------+--------------+ | v +--------------+--------------+ | 根据requiredModules依赖声明 | | 进行拓扑排序 | +--------------+--------------+ | v +--------------+--------------+ | | v v v +-----+----+ +-----+----+ +------+-----+ | prepare | | start | |notifyAfter | | (准备) |->| (启动) |->|Completed | | | | | |(完成通知) | +----------+ +----------+ +------------+ 图4:模块管理器初始化全流程

六、深入源码:ModuleDefine的实现案例

让我们看一个真实的模块定义——存储模块(StorageModule):

// 存储模块的核心定义publicclassStorageModuleextendsModuleDefine{publicstaticfinalStringNAME="storage";publicStorageModule(){super(NAME);}@OverridepublicClass<?extendsService>[]services(){// 存储模块暴露的服务接口List<Class<?extendsService>>services=newArrayList<>();services.add(IStorageDAO.class);// 数据访问对象services.add(IModelInstaller.class);// 数据模型安装器services.add(IHistoryDeleteDAO.class);// 历史数据删除// 如果存储支持聚合查询if(storageConfig.isSupportAggregation()){services.add(IAggregationDAO.class);}returnservices.toArray(newClass[0]);}}

再看看存储模块的Elasticsearch Provider是怎么注册这些服务的:

publicclassStorageModuleElasticsearchProviderextendsModuleProvider{publicStorageModuleElasticsearchProvider(){// 告诉框架"我是storage模块的elasticsearch实现"// 同时也声明了我的直接依赖addRequiredModuleDependency("core");// 运行时依赖core模块启动}@OverridepublicStringname(){return"elasticsearch";}@OverridepublicClass<?extendsModuleDefine>module(){returnStorageModule.class;}@OverridepublicModuleConfigcreateConfigBeanIfAbsent(){returnnewStorageConfig();// ES的配置类}@Overridepublicvoidprepare()throwsModuleStartException{// 1. 读取配置StorageConfigconfig=(StorageConfig)getModuleConfig();// 2. 初始化ES客户端this.elasticSearchClient=newElasticSearchClient(config.getClusterNodes(),config.getUser(),config.getPassword());// 3. 注册服务this.registerServiceImplementation(IStorageDAO.class,newStorageEsDAO(elasticSearchClient));this.registerServiceImplementation(IModelInstaller.class,newStorageEsInstaller(elasticSearchClient,config));this.registerServiceImplementation(IHistoryDeleteDAO.class,newHistoryDeleteEsDAO(elasticSearchClient));}@Overridepublicvoidstart()throwsModuleStartException{// 连接ES集群,验证连接elasticSearchClient.connect();}@OverridepublicvoidnotifyAfterCompleted()throwsModuleStartException{// 所有模块启动完成后,安装数据模型(创建索引等)IModelInstallerinstaller=getService(IModelInstaller.class);installer.install();}}

七、配置类的设计:类型安全的魔法

每个模块都有自己的配置类,继承自ModuleConfig。框架通过反射将YAML配置自动绑定到这些Java对象上:

// 存储模块的配置类publicclassStorageConfigextendsModuleConfig{// @Getter/@Setter 省略privateStringnamespace;privateStringclusterNodes;privateStringprotocol="http";privateStringuser;privateStringpassword;// 批量处理配置privateintbulkActions=2000;privateintbulkSize=20;privateintflushInterval=10;// 秒// 索引配置privateintindexShardsNumber=2;privateintindexReplicasNumber=0;// TTL配置privateintrecordDataTTL=7;// 追踪数据保留7天privateintminuteMetricsDataTTL=90;// 分钟指标保留90天privateinthourMetricsDataTTL=45;// 小时指标保留45天privateintdayMetricsDataTTL=45;// 天指标保留45天}

YAML配置中的值会自动映射到这些字段上,框架使用了SnakeYAML和反射技术实现。配置可以使用环境变量覆盖,这为容器化部署带来了极大的灵活性。


八、实战:理解模块的"热插拔"能力

模块化框架最强大的特性就是实现可替换性。以存储模块为例:

# 场景A:开发环境,用H2内存数据库exportSW_STORAGE=h2 ./bin/oapService.sh# 场景B:测试环境,用ElasticsearchexportSW_STORAGE=elasticsearchexportSW_STORAGE_ES_CLUSTER_NODES=es-test:9200 ./bin/oapService.sh# 场景C:生产环境,用Elasticsearch集群exportSW_STORAGE=elasticsearchexportSW_STORAGE_ES_CLUSTER_NODES=es1:9200,es2:9200,es3:9200 ./bin/oapService.sh

同一个OAP发行包,同一个启动脚本,只改变环境变量,就能适配完全不同的基础设施。这就是模块化框架的魅力——它把"选择权"交还给了运维者。


九、总结

OAP Server的模块化框架是整个SkyWalking可观测平台的地基。理解了这个地基,你就能明白为什么SkyWalking可以如此灵活地适应各种部署场景。

核心要点回顾:

  • ModuleDefine定义模块的"身份和能力",ModuleProvider提供"具体实现"
  • selector机制让运行时切换实现成为可能
  • 依赖声明+拓扑排序保证模块按正确顺序启动
  • 三阶段启动(prepare→start→notifyAfterCompleted)提供精细的生命周期管理
  • 配置类自动绑定让YAML配置直接驱动Java对象

下一篇我们将深入到OAP Server的启动流程,看看ModuleManager是如何一步步把零件组装成一台精密运转的机器的。


上一篇【第34篇】TraceId的生成算法——一个ID背后的精巧工程
下一篇【第36篇】OAP Server启动流程解析:从一行命令到一个可观测大脑的诞生


相关新闻

  • SSD UNC 坏块原理与分布式存储应对:从 LDPC 到 DIRECT 的 3 层防御
  • C++ noexcept操作符实战:7大场景解析与性能优化指南
  • ShaderGraph球面化节点深度解析:从数学原理到实战应用

最新新闻

  • Unity异步文件下载终极指南:UniTask实现断点续传与实时进度跟踪
  • Ollama 本地部署避坑指南:你遇到的报错,这里都有解药
  • 从零构建C++千核并行框架:无锁队列、NUMA感知与任务图调度实战
  • 抖音批量下载神器:3步搞定无水印视频批量保存完整指南
  • dnsmasq DHCP 服务配置:5分钟实现IP-主机名自动映射与本地DNS解析
  • 邻接表与邻接矩阵 C++ 实现对比:4种图操作场景下的内存与性能实测

日新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号