JAVA服務治理實踐之無侵入的應用服務監控--轉


原文地址:http://chuansong.me/n/603660351655

之前在分享微智能的話題中提到了應用服務監控,本文將會着重介紹Java環境下如何實現無侵入的監控,以及無侵入模式對實現各種技術架構統一服務治理的意義,還會破解“監控系統如何監控自己”的悖論。此次分享包含宜信眾多關鍵技術實踐和落地辦法,內容提綱如下:

1. 服務治理監控,機房監控,APM的區別與聯系

2. “無侵入”的應用服務監控

3. 無侵入引領統一服務治理

4. 打破悖論:監控系統如何監控自己

在開始之前,先解釋一下幾個概念。首先,APM(Application Performance Management)即應用性能管理,按照Gartner提出的抽象模式,它應該涵蓋以下內容:

  1. End User Experience:關注終端用戶對性能的真實體驗

  2. Runtime Application Architecture:應該反映應用的架構

  3. Business Transactions:能夠支持分析應用與用戶交互的操作事務

  4. Deep Dive Component Monitoring:深度應用診斷,特別是代碼級的性能追蹤

  5. Analytics / Reporting:按照業務模型對大量性能數據進行(實時)精確分析

下圖是Gartner對APM的抽象模型。APM領域目前已經有不少商用系統,比如國外的Dynatrace, IBM APM,國內的OneAPM,聽雲APM等等。

 

接下來說說機房監控。機房監控又被稱為IT監控,這是咱們最常見的監控系統類別,它一般涵蓋以下內容:

  1. 操作系統級監控:例如CPU、內存、網絡流量、磁盤IO、連接數、系統日志等等。

  2. 基礎設施監控:例如交換機、路由器負載、存儲設備IO等等。

  3. 基礎設施環境監控:例如UPS電源、動力監控、環境溫度、電力負荷監控等等。

以操作系統級監控為主的機房監控,目前有不少的實現,例如Nagios、Zabbix、Open-Falcon;針對基礎設施的監控以及環境的監控通常需要借助額外的硬件采集設備來完成。

最后說說服務治理監控。先簡單解釋一下什么是服務治理。服務治理是針對面向服務架構的系統進行監控和管理的過程。面向服務的架構包括傳統SOA,分布式服務,微服務等。

服務治理的核心內容涵蓋四個層面:

  1. 服務注冊與發現:服務接口信息被注冊到服務配置中心的過程;服務調用方可以通過服務唯一標識從服務配置中心查找服務接口信息。

  2. 應用/服務監控:針對應用以及服務接口進行監控,包括性能監控,業務指標監控,安全監控等。

  3. 服務SLA協調:評估並量化各個層面的服務等級指標,這些層面包括應用,應用實例,服務實例,服務接口等。

  4. 服務運行時控制:是服務之間調用過程的干預,包括路由規則,負載均衡,Failover切換,服務安全,保護降級,彈性伸縮。

服務治理的四個核心內容實際上也是一個層級關系(Layers Topology),上層的實現需要依賴下層的實現。

服務治理的監控主要涉及服務注冊與發現和應用服務監控兩個層級。在實踐中,我們又將這兩個層級擴展了輔助層級:

  1. 服務畫像:對服務接口特征信息(技術協議、入參出參、方法、類等)進行描述。

  2. 應用畫像:對應用的架構、組成、技術棧、部署信息進行描述。

  3. 應用上下文畫像:應用的運行是受上下文環境的影響,包括應用所處的容器(物理機OS、虛擬機、Docker等),同一個容器內的兄弟服務進程的狀態描述。

  4. 應用上下文監控:針對上下文環境的容器,同一容器內的兄弟服務進程的性能監控。

下圖展示服務治理的層級關系和服務治理監控包含的內容:

這三者由於關注點不同,所以在各自領域會有差異,但也有交集的地方。根據需要也可擴展各自的外延。

下圖展示了APM,服務治理監控,機房監控的區別與聯系:

“無侵入”的應用服務監控

這個部分主要介紹如何實現JAVA環境下的“無侵入”應用服務監控。如前文提到,要實現應用服務監控,就要先實現服務注冊。

經典的服務注冊方法有兩種:

  • 顯式配置:通過人工將服務的接口信息(服務名,服務URI等)通過配置的方式存儲到服務注冊中心。經典的WebService UDDI就是這種模式。這種方式的問題在於服務接口信息都是由人工收集的,出現滯后或者謬誤可能性高,較高的運維代價,無法適應快速迭代的節奏。

  • 代碼實現:通過代碼調用服務注冊中心客戶端,將服務的接口信息發送到服務注冊中心。使用ZooKeeper客戶端實現服務注冊就是這種模式。這種方式的進步之處在於服務接口的URI可能是通過代碼收集出來的,例如獲得IP,context路徑,接口相對地址從而拼接成服務接口的URI。

我們在早期服務化實現中,采用的是這種模式,但它的問題是需要代碼埋點,也就是“侵入”,這引發了如下問題:

  1. 與服務注冊中心客戶端的緊耦合:如果使用ZooKeeper,需要依賴它的jar包。

  2. 服務注冊代碼與服務接口代碼上下文緊耦合:必須在特定位置去使用服務注冊的代碼,而且可能還會包含特定服務的信息,這些信息可能是人工編排進去的。

  3. 由於不同系統是由不同團隊開發的,需要行政制度,“TopDown”規定服務注冊的編程,一旦有“不按套路出牌”的情況就會出現各種運維問題。

無侵入的服務注冊思路也用到了”微智能”的思想。

  • 全自動的收集應用實例,服務實例,服務接口的信息。這些信息包括應用唯一標識(AppID),服務名(Service ID),服務實例的URI,服務接口的URI,服務接口的元數據(類,方法,入參出參,注解,部署描述符),即自動發現。

  • 收集過程對應用透明,不可有任何直接依賴(API依賴,jar包依賴),對系統的研發團隊同樣透明,即無侵入。

  • 收集過程能夠自動適應應用,服務,服務接口的變化,即自我維護。

接下來分析一下JEE應用的特點:

  • 以應用服務器(Tomcat,Jetty,IBM WAS,JBoss)為容器,JEE應用的啟動或停止都被其應用容器感知。

  • 遵守JEE規范,服務以servlet,JAXWS,JAXRS等落地;或遵守“事實”規范,比如Spring,服務以Spring MVC落地。

  • servlet是HTTP的唯一入口(當然還有RMI,RPC等,其實類似,這里不做詳細展開)。

於是解決方案如下:

從應用服務器的層面來對應用進行畫像,即應用畫像,服務畫像。這個過程發生在每次應用啟動的時候,因為這樣就能自然捕獲應用的變化。就好比電影《星際穿越》里,從四維空間能夠更容易,更直接解決三維空間的問題,且更具通用性。

這里用到兩種技術:

  • 中間件劫持

  • 應用畫像技術

中間件劫持就是將我們自己的代碼行為植入到中間件的各種行為中。實現畫像和監控主要依靠四種關鍵行為:應用啟動,停止,接收請求,響應回復。

  • 應用啟動:用於應用畫像,服務畫像

  • 應用停止:失效服務摘除

  • 接收請求,響應回復:用於應用,服務監控(稍后會介紹)

對JEE應用服務器的劫持核心是掌控classloader tree,獲得優先加載權,從而可以改變這些行為。盡管各家實現不同,但其classloader tree結構基本類似。

下面以Tomcat為例進行說明,下圖是Tomcat的classloader tree:

值得注意的是,每個JEE應用都會被分配一個WebAppClassloader來加載其所有class。那么如果我們能夠感知應用啟動的行為,通過WebAppClassloader就可以收集到前文提到的各種畫像信息。

那么我們需要植入一個自己的classloader來獲取優先加載權。通過加載改寫后的class,來改變行為。我們把這個classloader成為UAVClassLoader(無人機類加載器)。

UAVClassLoader算法基本原理

  • UAVClassLoader創建時,將能夠讀取到的Class文件對應的Class名存儲到ClassMap中

  • 將TomcatLoader設置為UAVClassLoader的Parent

  • 將UAVClassLoader設置為TomcatLoader的一個屬性

  • 重寫TomcatLoader的loadClass方法

    1、如果UseUAVClassLoaderFlag為true,則使用UAVClassLoader.loadClass

    2、加載成功則返回Class

    3、失敗則使用TomcatLoader自己的loadClass

  • UAVClassLoader的LoadClass方法

    1、如果ClassMap中含有要加載的Class,則使用自己的findClass加載Class

    2、否則,將UseUAVClassLoaderFlag設置為false

    3、使用TomcatLoader.loadClass(注:這時TomcatLoader會直接用自己的loadClass)

  • 將UsePlusLoader Flag設置為true

具備中間件劫持能力之后,就可以進行應用畫像和服務畫像了。

JEE應用服務器的應用啟動實際是Web容器的創建過程。在Tomcat中的StandardContext就是Web容器的根類,在其加載的時候,UAVClassLoader會感知,通過改寫或bytecode weave手段在其start方法的最后植入代碼,完成兩個步驟:

  • 收集將Web容器的上文信息:包括WebAppClassLoader實例、Context Path、應用名、ServletContext、BasePath(應用實際路徑)、WorkDir(應用工作目錄)等。

  • 植入應用、服務畫像的代碼。

應用畫像包含了應用相關信息的收集,下面列舉幾個關鍵畫像信息:

  1. 應用標識(AppID):先取部署的應用名(由應用服務器配置決定),如果應用名為空則取Context Path(通常可能就是war包的名字),應用標識對於應用實例自動歸類有妙用,就是實現應用實例自動歸類。

  2. 應用名稱:使用WebAppClassLoader可以獲得web.xml的路徑,通過解析web.xml提取display-name(這個也是servlet規范),如果為空,則使用應用標識作為名稱。

  3. 應用的URI:應用URI=http(s)://<本地IP>:<端口>/。本地IP的獲取方式很多,這里不做說明;端口的獲取是通過劫持CoyoteAdapter(啟動監聽的入口)來獲得;Context Path已經獲得。

  4. 應用的類庫信息:通過WebAppClassLoader可以獲取所有類庫信息,通過類庫信息可以掌握應用的技術棧,可以擴展做很多有趣的事情。

服務畫像是按照技術規范(參見JEE應用的特點2),常見的技術規范:

  1. Servlet規范

  2. JAXWS規范

  3. JAXRS規范

  4. Spring規范

  5. RMI規范

  6. RPC規范(Netty,Thrift,Hessian等)

針對每種技術規范從3個方面進行收集:

  1. Class和Method:通過Java的反射方式提取信息,如服務類名,方法名,入參出參。

  2. Annotation:通過注解掃描工具提取具有相關注解的類,然后通過注解API提取注解信息。

  3. 部署描述符:通過WebAppClassLoader獲取web.xml, spring-config.xml, log4j.xml等部署描述符文件路徑,然后使用DOM解析提取關注的tag信息。

下面以Servlet為例對服務畫像過程進行說明:

  1. 使用FastClasspathScanner(輕量的開源類掃描工具)將帶有javax.servlet.annotation.WebServlet( Servlet 3.0的注解類)的Class掃描,這個過程需要WebAppClassLoader支持。並提取注解的信息(比如urlPatterns,loadOnStartup)。

  2. 通過WebAppClassLoader獲取應用的實際路徑(BasePath),而web.xml就在/WEB-INF下,加載web.xml提取所有的元素值(就是servlet class),同時也提取,等元素值。

  3. 對通過注解獲得的Servlet與通過web.xml獲得的Servlet進行合並。

     3.1  只有注解有或只有web.xml有的,直接保留。

     3.2  web.xml與注解重疊的servlet,保留web.xml的信息(Servlet規范,部署描述符替換注解)

  4. 對Servlet畫像數據進行整理

        4.1 ServiceID:由Servlet Class定義,保證唯一性

        4.2 Service URI:ServiceURI=<應用URI>+

JAXWS,JAXRS,SpringMVC等的畫像過程基本一致,主要區別在第1步時,需要通過注解提取“服務接口的相對路徑”信息。例如:

JAXWS需要服務名

ServiceURI=<應用URI>++

JAXRS或SpringMVC的每個服務接口路徑都是到方法級的

ServiceURI=<應用URI>++<class的path信息>+<method的path信息>

有同學可能會問:“講了半天,雖然我們已經拿到了應用,服務的畫像數據,哪服務注冊是怎么發生的呢?”這個問題會在第三部分揭秘。

接下來,看看如何實現“無侵入”的應用服務監控。應用服務監控實際上是對應用實例,服務實例,服務接口的性能指標進行捕獲的過程,常用的性能指標:響應時間,請求計數,錯誤計數,響應代碼計數等等。這些值的捕獲是發生在請求進入和出去的地方。

下圖是JEE應用服務器的Http CallFlow展示了HTTP的請求響應過程:

應用服務監控的落地方法:

  • 運用中間件劫持技術改寫CoyoteAdaptor.service()方法, 它負責整個Tomcat的請求處理,在方法開頭攔截請求,方法結尾攔截響應。這里可以監控應用服務器,應用,所有的URL的性能指標。

  • 運用中間件劫持技術改寫StandardWrapper.service()方法,它負責Servlet的請求處理,同上如法炮制。這里可以監控所有Http服務的性能指標(參見JEE應用特點3)。

總結起來,通過中間件劫持和應用畫像技術,可以輕松的實現對應用/服務的畫像以及監控。由於Tomcat的服務器架構比較古老,所以我們采用了改寫或bytecode weave的方式,但是也僅僅只是改寫了Tomcat的3個方法。如果是Jetty,可以通過實現它的InterceptChain來實現,完全沒有代碼改寫,只是增加了植入代碼。而Jboss可以通過擴展它的listener來實現,也沒有代碼改寫。

那么捕獲到這些數據之后如何監控呢?這也會在第三部分解答。

無侵入模式引領統一服務治理

無侵入模式除了解決前面提到的諸多技術問題以外,還與我們自身的實際需要相關。

  1. 面臨“微服務”的轉型關口,但十年沉淀下來上百的系統也需要時間來逐步重構,這個過程可能很長。

  2. 各個系統雖然基本都是JAVA的,但是使用的JEE技術還是各有不同的,比如JAXWS,JAXRS,純Servlet,SpringMVC,Thrift,SpringBoot等等。

  3. 系統架構差異大,包含單體架構,分布式服務架構,半SOA化架構,微服務架構。

  4. 系統的迭代很快,幾乎每天都有更新,新型技術棧引入的可能不斷增加。

從服務治理的角度,非統一的技術棧意味着像Dubbo之類統一技術棧的玩法不可行。所以“反轉”這個思路,把從以服務調用技術棧為中心的治理方式轉向以服務自動畫像與注冊為基礎,逐步接管調用鏈路的治理方式。

而對JEE應用完全無侵入的模式恰好適應了這一需求。

我們的服務治理系統的代號叫無人機(UAV),寓意是無人機能夠7*24的不間斷巡航,隨時隨地收集地理,建築,物品,人的變化,隨時隨地監控他們的行為,甚至精確的打擊或操控。

我們把UAV定位為統一服務治理的模式,一種與“服務調用”技術棧基本無關的治理方法。

下圖是UAV的架構圖:

接下來解答第二部分遺留的兩個問題:

  • 收集了應用和服務畫像之后,如何實現服務注冊

  • 捕獲到監控數據之后如何監控呢

先來看看UAV的捕獲流圖:

數據捕獲步驟:

  1. 無論是畫像數據,還是性能數據(實時數據)暫存在內存中。JEE應用服務器中可以暫存到MBean中

  2. 監控代理程序每隔5秒(可調節),采集Mbean中的畫像和性能數據

  3. 監控代理程序將收集的數據發送給消息系統

  4. 健康管理程序負責各種數據落地

  5. 同時健康管理程序會提取服務實例以及接口的信息注冊到緩存中,這個緩存作為服務注冊的存儲中心。由於每個一定周期(5秒)都有畫像數據推送,也意味着維持了服務心跳

下面的圖展示了實現細節:

通過UAV的捕獲流圖,可以發現在數據傳輸格式上采用了統一的Schema,實現對畫像數據,性能數據,日志數據的統一。這樣不僅僅統一了三種數據的傳輸,也對實現后期數據的各種轉換和處理提供了統一的處理模板,具有更好的擴展性。

下圖是性能監控數據的Sample:

下圖是畫像數據的Sample:

打破悖論:監控系統如何監控自己

監控系統通常都面臨一個問題:如何監控自己。監控系統不能監控自己的原因也很明顯,因為不能處理來自自身的異常。

其實破解之法也很直接,就是“冗余”。不過這里“冗余”不是簡單的冗余資源,而是在處理機制上實現“冗余”。

破解之法:雙通道+雙心跳

UAV采用雙通道+雙心跳的方式:

  1. 雙通道就是一條Http傳輸通道,用來傳輸容器/節點畫像數據和監控數據;一條MQ傳輸通道,用來傳輸實時數據,畫像數據,日志數據。

  2. 雙心跳是指不管來自Http通道還是MQ通道的數據實際上即可以看成業務數據,也可以看成心跳數據(遠端的節點還活着並且在工作)。

  3. 來自每個通道數據都會通過健康管理程序“簽到”。因此UAV的任何節點(監控代理程序,健康管理程序)出現宕機,都能夠被發現;並且它們的進程狀態,應用狀態也被自己監控。

這樣的做法並不是為了冗余而冗余,而是有以下考慮:

  1. 從分布式系統的考慮,UAV實現了心跳服務,並且允許多活,也允許多級心跳上行,那么Http通信方式更加適合這樣的場景;同時,Http通信意味着每次攜帶的數據payload不能太大,所以更適合容器,節點的畫像和監控數據,這些數據以及指標比較固定。

  2. 應用,服務的個數是未知的,且無論是畫像還是指標(性能,業務等)也可能很多,意味着數據payload可能較大,而MQ適合payload較大的場景;MQ可以一定程度保證數據有序,且隊列可以暫時持久化數據,防止了由於接收端宕機導致的數據丟失;同時“多活”的消費者,可以動態擴展,也能在某些消費者宕機后,快速接替繼續消費。

  3. 兩種通信方式意味着更高的可靠性,即便當某些服務不可用時,監控系統的另一部分依然可以繼續工作。例如如果UAV的實時數據服務都掛了,那么應用的性能數據就看不了,但是應用的進程(比如Tomcat)的性能數據還是能看的。

下面是一部分UAV實際監控的效果圖。

應用容器監控:包含UAV節點,所有服務進程,JAVA進程的監控

容器畫像,UAV節點畫像,進程畫像

進程監控:Tomcat為例

應用監控:應用,應用實例

應用畫像,服務畫像

應用監控

JEE應用服務器監控:

Tomcat為例

服務監控

應用日志監控

最后總結一下:

  • APM,服務治理監控,機房監控由於關注點不同,所以監控內容有差異,但也有交集。服務治理監控可以根據實際需要擴展外延到APM或機房監控的范疇。

  • “無侵入”應用服務監控的關鍵是實現中間件劫持和應用畫像技術。

  • 無侵入模式的玩法顛覆了傳統以服務調用技術棧為中心的服務治理模式,可以容納更多技術棧,具有更好的可擴展性,適合混合架構下的服務治理需要。

  • 監控系統如何監控自己的破解之法就是實現雙通道+雙心跳,從容器、進程的角度,或從應用,服務的角度來兩個維度來落地心跳和性能監控。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM