插件化架構(gòu)設(shè)計(jì)
插件化架構(gòu)另一種叫法是微內(nèi)核架構(gòu),是一種面向功能進(jìn)行拆分的可擴(kuò)展性架構(gòu),通常用于存在多個(gè)版本、需要下載安裝或激活才能使用的客戶端應(yīng)用;包含兩類組件:核心系統(tǒng)(core system)和插件模塊(plug-in modules)
- 核心系統(tǒng) Core System 功能比較穩(wěn)定,不會(huì)因?yàn)闃I(yè)務(wù)功能擴(kuò)展而不斷修改,通常負(fù)責(zé)和具體業(yè)務(wù)功能無(wú)關(guān)的通用功能,例如模塊加載等
- 插件模塊負(fù)責(zé)實(shí)現(xiàn)具體的業(yè)務(wù)邏輯,可以根據(jù)業(yè)務(wù)功能的需要不斷地?cái)U(kuò)展,將變化部分封裝在插件里面,從而達(dá)到快速靈活擴(kuò)展的目的,而又不影響整體系統(tǒng)的穩(wěn)定
如下圖所示:
Skywalking的插件化實(shí)現(xiàn)
Skywalking中每個(gè)組件的埋點(diǎn)實(shí)現(xiàn)就是一個(gè)插件,針對(duì)有差異的版本也可能一個(gè)組件有多個(gè)實(shí)現(xiàn),分別應(yīng)對(duì)不同的版本。
組件加載管理的核心流程
Skywalking是對(duì)這些組件埋點(diǎn)插件的加載管理的大致流程如下:
- 初始化配置,并明確插件所在的路徑。
- 在給定的路徑下查找并解析 skywalking-plugin.def 插件文件。
- 通過(guò)獨(dú)立的類加載器
AgentClassLoader加載插件類。 - 通過(guò)
PluginFinder對(duì)插件進(jìn)行管理分類。 - 使用 Byte Buddy 字節(jié)碼增強(qiáng)庫(kù)創(chuàng)建 AgentBuilder,其會(huì)根據(jù)已加載的插件動(dòng)態(tài)增強(qiáng)目標(biāo)類,插入埋點(diǎn)邏輯。
- 使用 JDK的SPI 加載并啟動(dòng)
BootService服務(wù)。BootService就理解為通常我們編寫(xiě)的Service服務(wù),就是功能的內(nèi)聚封裝。 - 添加一個(gè) JVM 鉤子,在 JVM 退出時(shí)關(guān)閉所有 BootService 服務(wù)。
下邊通過(guò)關(guān)鍵技術(shù)點(diǎn)的拆解,進(jìn)一步理解上述的流程。
1. 初始化配置
這里有3層配置,其過(guò)程以及覆蓋原理如下:
- 解析/agent/config/agent.config,獲取配置,填充到Config中
- 遍歷環(huán)境變量(即 System.getProperties() 集合),查找"skywalking." 開(kāi)頭的配置 更新Config中的值
- 解析 Java Agent 的參數(shù),更新Config中的值
2. 自定義ClassLoader
SkyWalking 的Agent 加載插件時(shí)使用到一個(gè)自定義的ClassLoader :AgentClassLoader,有了這個(gè)AgentClassLoader,就不會(huì)在應(yīng)用的 Classpath 中引入 SkyWalking 的插件 jar 包,來(lái)達(dá)到對(duì)插件的管理讓?xiě)?yīng)用無(wú)依賴、無(wú)感知的目的。
AgentClassLoader掃描并加載指定Agent目錄下的plugins和 activations 目錄中的插件jar包。
3. 解析插件定義
當(dāng)自定義類加載器掃描到插件jar,除了要加載類之外,還有最重要的一個(gè)目的,要明確的知道這個(gè)插件jar要增強(qiáng)組件中的哪些類以及如何增強(qiáng),而承載這些信息的類被skywalking約定在每個(gè)插件jar包的resource目錄下skywalking-plugin.def 文件中,其中其中每一行都是一個(gè)插件類的定義,如tomcat-7.x-8.x-plugin 插件中 skywalking-plugin.def 文件的內(nèi)容如下:
tomcat-7.x/8.x=org.apache.skywalking.apm.plugin.tomcat78x.define
.TomcatInstrumentation
tomcat-7.x/8.x=org.apache.skywalking.apm.plugin.tomcat78x.define
.ApplicationDispatcherInstrumentation
插件類中指定了待增強(qiáng)的類、方法,以及增強(qiáng)的邏輯,體現(xiàn)在AbstractClassEnhancePluginDefine類以及其內(nèi)部的幾個(gè)方法上:
- enhanceClass() 方法:返回的 ClassMatch,用于匹配當(dāng)前插件要增強(qiáng)的目標(biāo)類。
- define() :插件類增強(qiáng)邏輯的入口,采用了模板方法的設(shè)計(jì)模式,實(shí)現(xiàn)中會(huì)調(diào)用下面的 enhance() 方法和 witnessClass() 方法。
- enhance() :真正執(zhí)行增強(qiáng)邏輯的地方。
- witnessClass() :一個(gè)開(kāi)源組件可能有多個(gè)版本,通過(guò)該方法識(shí)別組件的不同版本,防止對(duì)不兼容的版本進(jìn)行增強(qiáng)。
- getConstructorsInterceptPoints() :構(gòu)造方法的切入點(diǎn),并指定增強(qiáng)的邏輯實(shí)現(xiàn)
- getInstanceMethodsInterceptPoints():實(shí)例方法切入點(diǎn),并指定增強(qiáng)的邏輯實(shí)現(xiàn)
- getStaticMethodsInterceptPoints():靜態(tài)方法切入點(diǎn),并指定增強(qiáng)的邏輯實(shí)現(xiàn)
4. PluginFinder
JavaAgent 啟動(dòng)時(shí)掛在的運(yùn)行機(jī)制是當(dāng)一個(gè)類被首次加載的時(shí)候,會(huì)給我們機(jī)會(huì)進(jìn)行字節(jié)碼增強(qiáng)處理,即回調(diào)transform方法,在這個(gè)方法中傳入被加載,待增強(qiáng)類,sw需找到跟這個(gè)類匹配的所有的插件類,這個(gè)工作就由PluginFinder來(lái)完成。簡(jiǎn)單來(lái)說(shuō)就是根據(jù)傳入的新加載的類,查找與其匹配的執(zhí)行增強(qiáng)處理的 AbstractClassEnhancePluginDefine 集合。
5. AgentBuilder
AgentBuilder 是 Byte Buddy 庫(kù)專門(mén)用來(lái)支持 Java Agent 的一個(gè) API,其作用是方便使用者配置增強(qiáng)哪些類,忽略哪些package,對(duì)于要增強(qiáng)的類,回傳給指定的transform方法執(zhí)行增強(qiáng),在這個(gè)transform方法中會(huì)調(diào)用PluginFinder的查找增強(qiáng)類的功能,找到增強(qiáng)類,然后通過(guò)其define方法對(duì)目標(biāo)類執(zhí)行增強(qiáng)。
6. BootService
Skywalking的Agent端,也需要跟OAPServer之間進(jìn)行各種交互以提供豐富完善的功能,比如trace數(shù)據(jù)的上傳,Metric數(shù)據(jù)上傳,配置的拉取等等。skywalking中約定,這些功能邏輯要內(nèi)聚后,封裝到不同的BootService中,比如:
- ConfigurationDiscoveryService //檢測(cè)配置更新
- KafkaJVMMetricsSender//向Kafka發(fā)送Metric
- KafkaTraceSegmentServiceClient//通過(guò)Kafka發(fā)送Trace
- TraceSegmentServiceClient//通過(guò)gRPC發(fā)送Trace
BootService是在Skywalking中由ServiceManager通過(guò)SPI的方式進(jìn)行管理,在META-INF.services中可以看到BootService全限定名的文件,文件中列出了當(dāng)前Jar包中的所有的暴露出去提供服務(wù)的BootService的實(shí)現(xiàn)類。

org.apache.skywalking.apm.agent.core.remote.TraceSegmentServiceClient
org.apache.skywalking.apm.agent.core.context.ContextManager
org.apache.skywalking.apm.agent.core.sampling.SamplingService
org.apache.skywalking.apm.agent.core.remote.GRPCChannelManager
org.apache.skywalking.apm.agent.core.jvm.JVMMetricsSender
org.apache.skywalking.apm.agent.core.jvm.JVMService
org.apache.skywalking.apm.agent.core.remote.ServiceManagementClient
org.apache.skywalking.apm.agent.core.context.ContextManagerExtendService
org.apache.skywalking.apm.agent.core.commands.CommandService
org.apache.skywalking.apm.agent.core.commands.CommandExecutorService
org.apache.skywalking.apm.agent.core.profile.ProfileTaskChannelService
org.apache.skywalking.apm.agent.core.profile.ProfileSnapshotSender
org.apache.skywalking.apm.agent.core.profile.ProfileTaskExecutionService
org.apache.skywalking.apm.agent.core.meter.MeterService
org.apache.skywalking.apm.agent.core.meter.MeterSender
org.apache.skywalking.apm.agent.core.context.status.StatusCheckService
org.apache.skywalking.apm.agent.core.remote.LogReportServiceClient
org.apache.skywalking.apm.agent.core.conf.dynamic.ConfigurationDiscoveryService
org.apache.skywalking.apm.agent.core.remote.EventReportServiceClient
org.apache.skywalking.apm.agent.core.ServiceInstanceGenerator
Skywalking中可以定義默認(rèn)的服務(wù)并且可以定義新的服務(wù)來(lái)覆蓋默認(rèn)的服務(wù),通過(guò)= BootService 上的 @DefaultImplementor 和 @OverrideImplementor 注解來(lái)實(shí)現(xiàn):
- @DefaultImplementor 注解用于標(biāo)識(shí) BootService 接口的默認(rèn)實(shí)現(xiàn)。
- @OverrideImplementor 注解用于覆蓋默認(rèn) BootService 實(shí)現(xiàn),通過(guò)其 value 字段指定要覆蓋的默認(rèn)實(shí)現(xiàn),比如KafkaJVMMetricsSender覆蓋JVMMetricsSender
@OverrideImplementor(JVMMetricsSender.class)
public class KafkaJVMMetricsSender extends JVMMetricsSender
ServiceManager 將統(tǒng)一初始化 BootServices 集合中的 BootService 實(shí)現(xiàn),同樣是在 ServiceManager#boot 方法中,會(huì)逐個(gè)調(diào)用 BootService實(shí)現(xiàn)的 prepare()、startup()、onComplete()方法
在 JVM 退出時(shí),通過(guò)鉤子關(guān)閉所有 BootService 服務(wù),通過(guò)調(diào)用ServiceManager的shutdown方法,遍歷其熟悉bootServices集合,并調(diào)用他們shutdown方法來(lái)關(guān)閉所有的BootService`。