快速排障,VI能幫你做什麼
來自專欄攜程技術中心
VI是什麼
一般情況下,在攜程我們是不建議研發同學直接從辦公網路訪問生產環境伺服器的。這樣做,除了安全方面的原因外,更重要的就是要維護生產環境機器運行環境的統一性。但這樣也給故障排除增加了一些複雜性,比如在排障過程中可能會遇到以下場景:
1. 明明我的 pom 里寫的依賴某中間件版本是 A,本地運行也沒問題,為啥到生產環境跑起來就感覺像依賴了版本 B?
2. 程序報了連接數不夠的異常,生產環境想看看系統參數,還要聯繫網站運營同學幫忙,要麼還要東奔西走申請各種伺服器許可權?
為了解決這個問題,同時為了能更快地進行故障排除,我們研發了一款中間件 —— VI。
VI 的全稱是 Validate Internal,直譯過來是「內部驗證」。看到這個名稱就會有同學問了,內部是哪裡的內部?驗證是要驗證什麼?
簡單來講,「內部」是指應用程序的內部,包括應用程序所處的環境、用到的框架等靜態依賴,以及CPU、內存使用情況等運行時狀態;而「驗證」則是指對程序的靜態依賴、運行時狀態進行實時監控,以輔助應用 Owner 來驗證當前應用狀態是否符合預期。
因此,對應用程序而言,VI 可以實時採集程序及容器狀態,並通過友好的可視化界面進行展示。
這段描述會讓不少同學想到比較常見的 Metrics 中間件。有很多 Metrics 中間件可以以 Json 等相對友好的形式,將用戶自定義的一些 Metrics 數據暴露出來;除此之外還有 Java 自帶的 JMX(Java Management Extensions),也可以通過註冊自定義的 MBean 來獲取各種程序內部信息。那麼,VI 相對於這些 Metrics 中間件的優勢在哪裡呢?
1. 強大的數據自定義展示功能: VI 提供了一套默認的數據展示模板,用戶只需以簡單的 api 調用將待展示的數據交給 VI,就可以在本機的 VI 界面上看到友好的展示和交互:
2. 完備的容器信息採集:就我們常見的運行環境而言,業務代碼的運行,自上而下會用到很多容器:Spring IOC,Tomcat,JVM,Docker等。常見的 Metrics 中間件多數只提供了基本的 Metrics 框架,很少包含具體的數據採集模塊。
但是,這些容器的狀態往往會對業務邏輯的運行產生很大的影響。每個用戶都增加完整的數據採集模塊沒有必要,但排障時再想添加又已經晚了,而且普通用戶也並沒有許可權登錄生產環境去查看這些數據。對於這類情況,VI 可以以更加友好的方式,提供非常完備的支持:
3. 一站式自助排障體驗:對同一個應用而言,不同團隊的關注點是不同的。產品經理關注用戶體驗,業務開發關注業務邏輯,框架關注穩定性和性能,運維關注容器各項指標。所以,一旦有用戶反饋程序有問題,那麼我們的排障之路就會很漫長。
基於友好的交互界面和完備的基本信息採集,VI 在一定程度上有助於緩解這個問題。VI 可以幫助你從應用的角度而不是從「理論上」的角度來看應用程序運行時真實感受到的環境、框架、容器的方方面面,而且不需要基礎團隊的支持、不需要申請各種許可權,也不需要為了查看不同的數據在各種工具間轉來轉去,顯著地節約了問題定位耗時,減少因排障速度而增加的業務損失。
4. 發布系統支持:完善的發布流程生命周期控制,可以很大程度上減少因發布而引發的生產故障。在這一點上,VI 通過自定義點火/健康檢測組件,為發布流程的生命周期控制提供了必要的支持。
除了可以自定義業務點火邏輯,各種常見的公共框架組件也集成了 VI 的點火組件。通過與發布系統集成,從流程上控制了點火/健康檢測失敗的應用不會在生產環境提供服務。
雖然 VI 可以暴露很多底層細節情況,但 VI 本身與我們常見的監控系統(例如CAT) 還是有很大區別的,主要表現在以下方面:
1. 歷史追溯:VI 是無法追溯歷史監控數據的,用戶只能從 VI 獲取到實時的監控數據。之所無法追溯歷史數據,是因為 VI 的設計目標之一是幫助用戶快速排除當前正在發生的故障,並不關心歷史曾經出現的問題。而且考慮到有些監控數據的獲取代價較高,且業務正常時並無太大參考價值,所以 VI 被設計為只有當用戶存在訪問行為時,才開始採集數據,當用戶訪問行為結束後即關閉數據採集。
2. 告警:告警功能幾乎是監控系統的標配,但 VI 並不具備這樣的能力,原因是 VI 並未在後台持續採集數據,因而並沒有衡量系統指標變化的能力,所以也無法針對指標變化提供告警功能。
3. 數據處理/分析能力:VI 並沒有中央節點來處理/分析應用的全局數據,因而無法從宏觀上對應用健康情況進行評價,而這一能力是完善的監控系統需要考慮到的。
因此,雖然 VI 可以實時採集很多監控數據,但 VI 的設計目標並不是成為一個監控系統,而是幫助用戶快速定位/解決問題的工具。
接下來的內容會向大家簡單介紹下 VI 兩個基本功能(交互設計/在線調試)的設計細節,供大家參考。
交互設計
VI 的使用方式非常簡單,常見的Web 應用只需要添加 Maven 依賴,應用啟動後即可通過VI Portal 或直接 IP 訪問特定 URL 來使用 VI。那麼 VI 是怎麼實現只增加一個Jar 包依賴就可以提供頁面交互呢?是否所有類型的應用都可以這麼簡單地使用 VI 的頁面交互?
1. 依賴:VI 接入最簡單的方式就是只添加 Maven 依賴,而無需進行配置和額外代碼編寫。這種接入方式條件就是需要使用servlet 3.0及以上版本,默認已被 Tomcat 7 及以上版本所支持。
對大多數業務應用場景來說,這個條件是非常容易達成的。但如果應用並沒有使用 Web 容器,就需要根據實際情況,增加 vi-server 依賴或者 vi-netty 依賴。其中,vi-server 依賴內嵌了 jetty,本質也是通過啟動一個內嵌的 web 容器來實現界面交互;vi-netty 則使用了 netty 自帶的 http handler,為應用程序監聽的埠增加了 http 協議處理能力,以啟用 VI 界面交互。
2. 入口:VI 的交互界面本身並未跳出J2EE 的規範,因此其入口也僅根據運行環境不同略有變化,這部分對用戶是基本透明的。入口的基本思路是,在應用程序生命周期儘可能早的地方,進行且只進行一次初始化;儘可能利用常見框架來帶動完成 VI 的初始化動作。
3. 路由:VI 路由動作分為兩塊,靜態資源路由和數據路由。其中,靜態資源路由負責將 http request 請求的靜態資源,從 jar 包的 resources 中載入並 response 給請求方;數據路由則有點像 dispatcher 的角色,根據不同的 api 路徑,找到對應的 component,從中獲取數據並反饋給請求方。
4. 組件:VI 的內容生產者。根據需求,組件大致可以分為控制型組件和數據型組件兩種類型。其中,控制型組件是會影響到程序狀態的,比如點火組件如果點火失敗,程序是不會對外提供服務的,這類組件可能並不是由外界請求觸發,而是在 VI 整體初始化的時候就被觸發了。另一類數據型組件則不會影響到程序狀態,數據型組件只被動地收集數據,而且只有在真正使用 VI 時才會開始收集,以期儘可能減少對應用程序的影響。
從上述描述可以看出,VI 交互設計主要遵循了幾個基本原則:
1. 盡量避免侵入業務程序:這個也是目前各種中間件都盡量遵循的原則之一。這樣做有幾個好處,介面越少,用戶的學習成本就越低;介面越簡單,濫用和誤用的可能性就會越少;業務代碼無侵入,就意味著中間件代碼與業務代碼耦合性會很低,排障難度也會變小。
2. 模塊化/插件化:VI 允許業務自定義點火邏輯,它自身的點火邏輯也是通過同樣的機制來實現的;VI 允許業務自定義 metrics,它本身也通過 metrics 組件提供出很多常見的 metrics 出來。因此,對 VI 來說,很多功能,只是定義了一層 SPI,用戶可以自己去實現這套 SPI,甚至 VI 本身很多功能也按照同樣的邏輯來實現的。這麼做的好處是靈活、層次結構分明,便於維護和擴展。
3. 儘可能少的資源消耗:對應用程序來說,VI 算是輔助類型的組件,因此,VI 不應該對應用程序的穩定性產生不好的影響。考慮到這一點,VI 只有在有用戶打開界面時才實時開啟數據採集功能,且對整體內存消耗做了嚴格的限制,盡最大的努力以減少 VI 本身對應用程序資源的消耗。
在線調試
我們有時候會遇到這種問題,同一份代碼,在測試環境運行得很好,一旦部署到生產環境就會偶爾出現意外的問題,排除掉代碼和運行環境的問題後,一般會猜想是否上下游的某些數據交互存在問題。但不巧的是,可能對應的數據處理邏輯事先並未考慮到監控埋點。這種時候我們可能不得不修改代碼,增加監控埋點,重新打包發布,再驗證先前的猜想,而猜想驗證失敗後,很可能又要從頭再來一遍。
VI 的在線調試功能可以極大地降低這種情況下的排障負擔。
所謂的在線調試並不是真的給應用加個斷點把應用阻塞住,而僅僅是給業務代碼的某個類某一行加個標籤。VI 會在標籤生效後,動態修改對應類的位元組碼,並促使 JVM 重新載入該類。VI 修改的類位元組碼部分非常簡單且可靠,沒有任何變更操作,只採集了對應標籤的上下文信息,通過交互界面展示給用戶,告訴用戶斷點位置的上下文情況。
所以,這個功能的難點在於如何在程序運行時動態修改位元組碼。
JDK 的 java/lang/instrument 包提供了這種功能。JDK 對這個包的說明是這樣的:
Provides services that allowJava programming language agents to instrument programs running on the JVM. Themechanism for instrumentation is modification of the byte-codes of methods.
也就是說這個包,通過提供位元組碼修改的功能,為Java Agent 提供了操縱運行時程序的能力。
如果有了解過 Java Agent,應該知道通常我們要為 JVM 增加 Agent,是通過增加JVM 參數來實現的,VI 在線調試的第一版就是這樣做的。這樣做有個問題,需要每個希望使用在線調試功能的同學都修改自己的 JVM 參數;或者需要 OPS 同學幫忙統一添加 JVM 參數,無論哪種方式都不符合 VI 儘可能簡化接入方式的目標。所以 VI 在隨後的版本,改為使用反射的方式,通過調用 HotSpotVirtualMachine的 loadAgent 方法,來實現動態載入 JavaAgent 的能力。
自此,VI 的在線調試功能就變得很好用了,無痛接入,界面友好,動態啟用。用戶不必通過重複猜想-埋點-打包上線-驗證猜想的過程,就可以獲取想要的信息,極大地縮短了此類問題的排障時間。
結語
做程序員久了,誰沒寫過幾個 BUG,有 BUG 不可怕,找不到 BUG 才最可怕,因為定位問題太過耗時而影響到用戶更可怕。
這麼可怕的事情,攜程VI 希望能盡最大努力幫到程序猿們。如果大家對 VI 有什麼產品建議,歡迎在評論區留言交流。
【作者簡介】宋通,攜程框架研發資深工程師,參與過分散式消息系統等多個中間件及框架產品的設計與研發,對分散式系統設計及程序性能優化有持續的興趣。
更多來自攜程技術人的一手乾貨,歡迎搜索關注「攜程技術中心」微信公號。
推薦閱讀:
※(譯) IBM WAS 6.1 Web container problem determination
※如何配置WAS服務跟隨系統啟動
※什麼是redis?Redis的各項功能解決了哪些問題?以及redis的入門實戰
TAG:中間件 |