一 基本術語
Trace : 表示系統中一次數據或者執行路徑的軌跡,可以簡單認爲是多個Span有向無環圖。
Span:Span代表系統中一次邏輯操作運行單元。Span之間會簡歷嵌套或者順序排列建立邏輯因果關係
二 組件架構
無緩存隊列的架構
有緩存隊列架構
三 服務追蹤
服務追蹤過程
四 應用埋點
1 手動埋點工具Client
針對非java應用,目前只能通過手動埋點的方式上報服務追蹤信息,對應的client列表如下 :
Language |
GitHub Repo |
Go |
|
Java |
|
Node.js |
|
Python |
|
C++ |
|
C# |
2 手動埋點過程
1. 添加依賴 主要添加opentracing 和 jaeger相關依賴
2. 初始化Tracer對象,並註冊到GlobalTracer
io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("demo"); io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration(); // endpoint 爲jaeger collector url :14268/api/traces sender.withEndpoint("http://collertor-host:14268/api/traces "); config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1)); config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000)); GlobalTracer.register(config.getTracer());
|
3. 具體方法體內部獲取Tracer對象,並記錄請求數據
Tracer tracer = GlobalTracer.get();
// 創建 Span
Span span = tracer.buildSpan("parentSpan").withTag("myTag", "spanFirst").start();
tracer.scopeManager().activate(span,false);
tracer.activeSpan().setTag("methodName", "testTracing");
// 業務邏輯
...
span.finish();
|
4. 多個微服務間通過Context傳遞Trace信息,比如在HTTP請求中使用Extract/Inject 方法在 HTTP Request Headers 上透傳數據
//客戶端Inject
tracer.inject(tracer.activeSpan().context(), Format.Builtin.HTTP_HEADERS, new RequestBuilderCarrier(requestBuilder));
//服務端Extract
parentSpanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapExtractAdapter(headers));
3 Java應用自動埋點
目前OpenTracing API社區已經實現了支持各種java框架的自動埋點:
針對不同的框架,需要開發對應的OpenTracing API 插件用來實現自動埋點。以Spring Cloud框架爲例子,簡單說明下OpenTracing API 埋點實現過程。
4 Spring Cloud 埋點實現
主要實現原理利用Spring AOP切片技術抽象埋點行爲,如代碼所示聲明瞭TraceAsyncAspect 切面類,使用@Around 聲明攔截規則,後面的邏輯與手動埋點類似,創建一個span,將業務邏輯包圍起來即可。
5 半自動埋點
Spring 應用埋點步驟
- pom.xml 添加依賴
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-cloud-starter</artifactId>
</dependency>
- 項目中定義OpenTracing Tracer Client Bean
@Bean
public io.opentracing.Tracer tracer() {
//return new tracer instance of your choice (Zipkin, Jaeger, LightStep)
return new Configuration("spring-boot", new Configuration.SamplerConfiguration(ProbabilisticSampler.TYPE, 1),
new Configuration.ReporterConfiguration())
.getTracer();
}
在現有項目或者新建項目中完成上述兩步即可完成Spring 應用的自動埋點
其他實現
- 添加依賴
- 配置參數 application.properties
spring.application.name=theservicenname
opentracing.jaeger.udp-sender.host=localhost
opentracing.jaeger.udp-sender.port=6831
- 上述兩步即可完成服務追蹤埋點,如果想實現手動埋點,可以通過@Autowired 註解 在對應的類上定義Trace變量即可,這樣可以在具體的類方法上手動埋點。
@Autowired
Tracer tracer;
6 全自動埋點
利用javaAgent機制和字節碼技術實現對代碼無侵入的收集埋點信息,上傳到分佈式追蹤系統Jaeger、skywalking、等。即通過java代理攔截器實現埋點。使用方法很簡單僅需要在java 啓動命令前 添加 javaagent 參數命令即可。
java -javaagent:{agentpath} -jar MyApp.jar
Java-specialagent
java-specialagent 是基於bytebuddy字節碼框架開發的自動埋點Agent
目前最新穩定版 1.7.0
簡介
Java-SpecialAgent 能夠動態或者靜態的綁定java應用,實現自動埋點。通過整合不同的java框架(spring,es,kafka)埋點規則和不同的Tracer Exporter(jaeger、LightStep、wavefront)來實現自動埋點
整合方式
- 與Java-SpecialAgent代碼實現沒有耦合性,SpecialAgent需要實現集成規則來與社區 opentracing-contrib 上已經實現好的java框架埋點工具集成來實現自動埋點,這種方式也是該項目推薦的方式。
- 與Java-SpecialAgent代碼實現完全耦合性,在SpecialAgent內部實現某種java框架的埋點規則,用戶想使用的話,必須只能通過Java-SpecialAgent 代碼啓動應用進行埋點。
Trace Exporters
SpecialAgent 通過 java的SPI機制來創建不同的分佈式追蹤系統的client,以jaeger爲例說明。
<isolatedDependencies>
...
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>jaeger-client-bundle</artifactId>
</dependency>
...
</isolatedDependencies>
使用方式
- 靜態綁定
java -javaagent:opentracing-specialagent-1.7.0.jar -jar MyApp.jar
- 動態綁定
// jdk 1.8
java -Xbootclasspath/a:$JAVA_HOME/lib/tools.jar -jar opentracing-specialagent-1.7.0.jar ${PID}
// jdk 1.9
java -jar opentracing-specialagent-1.7.0.jar ${PID}
- 靜態延遲綁定,應用優先啓動,然後在綁定SpecialAgent
// 目前僅支持 SpringBoot Spring WebMVC
java -javaagent:opentracing-specialagent-1.7.0.jar -Dsa.init.defer=true -jar MySpringBootApp.jar
全自動埋點實現原理
ByteBuddy 是 Java 運行時字節碼生成庫,本質上是AOP編程實現。
舉例說明
- 定義 Log 註解
- 定義 字節碼類LoggerAdvisor
- 定義測試類
- 執行結果
Java-specialagent具體實現
埋點集成
通過bytebuddy字節碼API集成社區已經實現好的java框架自動埋點。
- 加載啓動配置 AssembleUtil.loadProperties()
- 設置Agent Instrumentation 有加載到JVM的類所有操作權限 BootLoaderAgent.premain(inst)
- 加載第三方埋點實現 AgentRule.$Access.load()
load(instrumenter.manager, ruleFiles, isoClassLoader)
埋點規則舉例
設置規則
攔截器實現
埋點上報
通過java SPI機制完成追蹤信息上報的分佈式追蹤系統。目前支持的分佈式追蹤系統包括:
Trace Exporter |
Java-specialagent 集成 java-tracerresolver 項目實現不同 Trace
Exporter 加載, 在 java-tracerresolver 內工具類 TracerResolver 中包含靜態方法 resolveTracer(),通過該方法查找實現 TracerFactory 接口方法的 Trace Exporters。
不同類別的Trace Exporters 都需要統一實現 TracerFactory 方法,以Jaeger 爲例,代碼實現如下:
Java-specialagent 在根POM 中的 <isolatedDependencies> 配置指定的依賴項,實現Trace Exporters 與 Java-specialagent 的集成。
java-specialagent 在應用啓動之前會預加載Trace Exporters, Trace Exporters 根據配置區分。
加載過程
Jaeger舉例
java -javaagent:opentracing-specialagent-1.7.0.jar -Dsa.tracer=jaeger -Dsa.log.level=INFO -DJAEGER_SERVICE_NAME="test" -jar springboot-helloworld-1.0-SNAPSHOT.jar
五 SkyWalking實現原理
1 SkyWalking簡介
分佈式系統的應用程序性能監視工具,專爲微服務、雲原生架構和基於容器架構而設計,開源Mosn在未來的計劃中也要納入SkyWalking。
SkyWalking 是觀察性分析平臺和應用性能管理系統。提供分佈式追蹤、服務網格遙測分析、度量聚合和可視化一體化解決方案.支持Java, .Net Core, PHP, NodeJS, Golang, LUA語言探針支持Envoy + Istio構建的Service Mesh。
2 技術架構
3 實現原理
SkyWalking Agent 同樣也是基於 JavaAgent 機制和字節碼增強技術,實現應用自動埋點。
SkyWalking Agent啓動過程
1 初始化Agent配置 SnifferConfigInitializer.initialize(agentArgs)
2 加載Agent插件 new PluginFinder(new PluginBootstrap().loadPlugins())
3 通過ByteBuddy初始化 Agent Instrumentation
4 初始化服務管理 ServiceManager.INSTANCE.boot()
插件化實現透明埋點
SkyWalking Agent 提供了多種插件,實現不同框架的埋點。
插件的加載
通過AgentClassLoader 繼承 java.lang.ClassLoader 實現Agent 類加載器。應用透明接入 SkyWalking ,不會顯示導入 SkyWalking 的插件依賴。通過實現自定義的 ClassLoader ,從插件 Jar 中查找相關類。例如說,從 apm-dubbo-plugin.jar 查找 org.skywalking.apm.plugin.dubbo.DubboInstrumentation
插件的匹配
插件資源解析器 PluginResourcesResolver 讀取所有插件的定義文件。插件定義文件必須以 skywalking-plugin.def 命名,例如 :
再讀取 skywalking-plugin.def 文件,生成插件定義,添加到 pluginClassList
插件的攔截
AbstractClassEnhancePluginDefine ,類增強插件定義抽象基類。不同插件通過實現 AbstractClassEnhancePluginDefine 抽象類,定義不同框架的切面,記錄調用鏈路。以 Spring 插件爲例子,如下是相關類圖 :
埋點插件擴展
public interface InstanceMethodsInterceptPoint { //定義切向方法的適配器,符合適配器的class將被增強 ElementMatcher<MethodDescription> getMethodsMatcher(); //增強的具體實現類,classReference String getMethodsInterceptor(); //是否重寫參數 boolean isOverrideArgs(); } |
定義攔截點
實現一個增強類
public interface InstanceMethodsAroundInterceptor { //方法真正執行前執行 void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable; //方法真正執行後執行 Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable; //當異常發生時執行 void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t); } |
總結
對比
對比項 |
java-specialagent |
skywalking |
支持多語言 |
是 |
是 |
支持擴展框架 |
是 |
是 |
支持java應用自動埋點 |
是 |
是 |
支持OpenTracing |
是 |
是 |
GitHub Start |
108 |
13k |
告警支持 |
否 |
是 |
JVM監控 |
否 |
是 |
用戶數 |
少 |
多 |
插件數 |
45+ |
60+ |
是否國內人員開發 |
否 |
是 |
結論
Java-specialagent和skywalking 都是通過javaAgent機制和字節碼增強機制來實現java應用的無侵入埋點。但是,從用戶使用情況和社區支持情況來看skywalking 要優於java-specialagent,skywalking在文檔和插件擴展開發方面也要優於java-specialagent。
java-specialagent 支持自動埋點列表 https://github.com/opentracing-contrib/java-specialagent#41-integrations
skywalking 支持自動埋點列表 https://github.com/apache/skywalking/blob/v7.0.0/docs/en/setup/service-agent/java-agent/Supported-list.md