一、Pinpoint 是什麼
Pinpoint 是一款全鍊路分析工具,提供了無侵入式的調用鍊監控、方法執行詳情檢視、應用狀态資訊監控等功能。基于Google Dapper論文進行的實作。
核心思想就是在服務各節點彼此調用的時候,記錄并傳遞一個應用級别的标記,這個标記可以用來關聯各個服務節點之間的關系。
比如兩個節點之間使用 HTTP 作為請求協定的話,那麼這些标記就會被加入到HTTP頭中,各應用的Agent在進行上報的時候,将該标記以及對應的上下級應用上報到Pinpoint collector中,通過該标記辨別請求,并将各個應用串聯成完整的調用鍊路。
Pinpoint的特點如下:
- 分布式事務跟蹤,跟蹤跨分布式應用的消息;
- 自動檢測應用拓撲,幫助你搞清楚應用的架構;
- 水準擴充以便支援大規模伺服器叢集;
- 提供代碼級别的可見性以便輕松定位失敗點和瓶頸;
- 使用位元組碼增強技術,添加新功能而無需修改代碼。
Pinpoint針對不同的元件提供了豐富的插件,其支援以下子產品:
- JDK 6+
- Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5
- Spring, Spring Boot (Embedded Tomcat, Jetty)
- Apache HTTP Client 3.x/4.x, JDK HttpConnector, - GoogleHttpClient, OkHttpClient, NingAsyncHttpClient
- Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER
- MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA
- Arcus, Memcached, Redis, CASSANDRA
- iBATIS, MyBatis
- DBCP, DBCP2, HIKARICP
- gson, Jackson, Json Lib
- log4j, Logback
二、插件知識和相關的資料結構
pinpoint插件能夠在代碼級别對感興趣的方法進行攔截,可以針對業務代碼,第三方包等,如記錄方法的執行時間,參數,方法傳回結果,在RPC調用中插入辨別ID以記錄調用關系等, pinpoint插件很容易擴充,官方也提供了很多插件,基本覆寫了常用的元件,如hystrix,dubbo等,部署即可用。
1、插件結構
Pinpoint 插件由TraceMetadataProvider 和 ProfilerPlugin 的實作組成。TraceMetadataProvider 實作給 pinpoint agent, collector, web元件提供 ServiceType 和 AnnotationKey,ProfilerPlugin 實作用于agent轉換目标類以記錄追蹤資料。
Pinpoint 插件以 jar 檔案的形式部署。Agent 在 plugin 目錄下用 ServiceLoader 搜尋TraceMetadataProvider 和 ProfilerPlugin 的實作,web 和 collector 在 WEB-INF/lib目錄下搜尋,ServiceLoader 要求 provider 配置檔案存在于 META-INF/services 目錄下,是以在 plugin jar 中必須放置以下檔案,實作類通過 Java 的服務發現機制進行加載。
2. 介紹下幾種關鍵的類
2.1 TraceMetadataProvider
TraceMetadataProvider 實作類中提供 ServiceTypes 和 AnnotationKeys。
2.1.1 ServiceTypes
每個 Span 和 SpanEvent 都包含一個 ServiceType,這個ServiceType表示跟蹤方法所屬的庫,以及跟蹤它的Span和spanevent應該如何處理。
下表顯示ServiceType包含哪些屬性:
Pinpoint 為了盡量壓縮 Agent 到 Collector 資料包的大小,ServiceType 被設計成不是以序列化字元串的形式發送的,而是以整形數字發送的 (code 字段),這就需要建立一個映射關系,将 code 轉換成對應的 ServiceType 執行個體。
這個映射機制就是由 TraceMetadataProvider 負責的。
ServiceType code必須是惟一的。如果要編寫一個将被公開共享的插件,就必須聯系pinpoint團隊來獲得配置設定的ServiceType code。
如果所開發的插件是私有的,那可以從下面的表格中選擇一個ServiceType code,如一會我們要展示的示例中一樣。
公開的ServiceType Code範圍:
私有的ServiceType Code範圍:
ServiceType 還有一些屬性:
2.1.2、AnnotationKey
Annotation 是包含在 Span 和 SpanEvent 中的更詳盡的資料,以鍵值對的形式存在,鍵就是 AnnotationKey,值是基本類型,String或者byte[]。
Pinpoint 内置了很多的 AnnotationKey,如果不夠用的話也可以通過 TraceMetadataProvider 來自定義。AnnotationKey 的資料結構如下:
2.2、ProfilerPlugin
ProfilerPlugin修改目标庫的類來收集跟蹤資料。插件的工作原理:
- Pinpoint Agent 随 JVM 一起啟動;
-
Agent 加載所有 plugin 目錄下的插件;
Agent 調用已加載的插件的; ProfilerPlugin.setup(ProfilerPluginSetupContext) 方法;
- 在 setup 方法中,插件定義那些需要被轉換的類并注冊回調TransformerCallback;
- 目标應用啟動;
- 每當類被加載的時候,Pinpoint Agent 會尋找注冊到該類的回調 TransformerCallback;
- 如果 TransformerCallback 被注冊,Agent; 就調用它的 doInTransform 方法;
- TransformerCallback 修改目标類的位元組碼 (例如添加攔截器、字段等);
- 修改後的代碼傳回到; JVM,類被加載的時候使用修改後的位元組碼;
- 應用程式繼續;
- 當調用到被修改的方法的時候,已注入的攔截器的 before 和 after 方法會被調用;
- 攔截器記錄追蹤資料.
最重要的幾點可以歸結為:
1)找出哪些方法值得跟蹤。
2)注入攔截器來實際跟蹤這些方法。
這些攔截器用于提取、存儲和傳遞跟蹤資料,然後将其發送給收集器。攔截器甚至可以互相協作,在它們之間共享上下文。
插件還可以通過向目标類添加getter或定制字段來幫助跟蹤,以便攔截器在執行期間可以通路它們。
三、位元組碼注入怎麼工作的
由于位元組碼技術必須處理java位元組碼,它會增加開發的風險而降低生産效率。此外,研發人員容易犯錯誤。在Pinpoint中,通過攔截器抽象提高了生産力和可通路性。
Pinpoint中注入必要的代碼,通過在類裝入時插入應用程式代碼來跟蹤分布式事務和性能資訊。由于跟蹤代碼直接注入到應用程式代碼中,是以提高了性能。
在 Pinpoint中,API截取和資料記錄是分離的。如上圖,攔截器被注入到我們希望跟蹤的方法中,在該方法前後調用before()和after()處理資料記錄。
通過位元組碼指令, Pinpoint Agent可以僅從必要的方法記錄資料,進而使分析資料的大小變得緊湊。
下面就根據以上原理來實作一個插件,該插件能夠攔截配置的方法。
先定義些常量類型, 設定ServiceType 和 相應的code,AnnotationKey 和相應的code:
定義資料類型類用于讀取配置檔案中的配置,檔案中的配置規則為:
有參數方法
package.Clazz.MethodArgs=arg1,arg2
無參數方法
package.Clazz.MethodArgs
每行一個配置項:
定義 GeneralConfigMetadataProvider 提供 ServiceType 中繼資料:
定義針對方法的攔截器 GeneralConfigInterceptor 繼承自 SpanEventSimpleAroundInterceptorForPlugin,這裡在方法執行後簡單記錄方法的名稱,參數,傳回值
最後重要的是我們的插件,傳入配置和轉換模闆 transformTemplate:
META-INF/services目錄下添加插件和中繼資料檔案:
com.navercorp.pinpoint.plugin.ProfilerPlugin
--com.navercorp.pinpoint.plugin.general.config.GeneralConfigPlugin
com.navercorp.pinpoint.common.trace.TraceMetadataProvider
--com.navercorp.pinpoint.plugin.general.config.GeneralConfigMetadataProvider
當以上核心的代碼寫完後,将插件以jar的形式部署在準備好的 agent 目錄中,啟動項目,在 project.propertie 中配置要攔截的方法如 com.bestpay.middleware.service.StudentManagerImpl.getAllStudents。在請求的路徑中能看到以下的資訊。配置我們配置的方法被攔截到了。
至此,我們的小插件講解就結束了,此為抛磚引玉,Pinpoint 提供了豐富的插件開發 API,如攔截異步方法、調用鍊跟蹤、攔截器之間共享資料等,有興趣的同學可進一步探索。
原文釋出時間為:2018-08-01
本文作者:吳振
本文來自雲栖社群合作夥伴“
高效運維”,了解相關資訊可以關注“
”