調用鏈系列(2):輕調用鏈實現

一、前言

調用鏈系列(1):解讀UAVStack中的貪吃蛇

上篇文章分享了一下調用鏈的模型設計及模型時序圖。相信大家通過上一篇文章對調用鏈有了一個整體上的瞭解,如:調用鏈是什麼、能做什麼及整體實現策略。

這篇文章我們繼續介紹調用鏈的服務端信息收集以及服務間上下文傳遞。

二、服務端信息收集

服務端信息收集整體流程如下圖所示,通過在應用容器(tomcat等)啓動過程中植入切點從而實現在應用邏輯執行之前和之後對請求進行劫持。

  • 應用邏輯執行之前:解析request中調用鏈信息,並初始化調用鏈上下文;
  • 應用邏輯執行之後:解析response中調用鏈信息,並將本次請求處理的所有調用鏈信息輸出到日誌文件。

三、切點植入

在介紹切點之前我們應該對servlet容器(本文以tomcat爲例)處理一次請求的大致流程有一個整體的瞭解。

圖片來源於網絡

在Connector接收到一次連接並轉化成請求(Request)後,會將請求傳遞到Engine的管道(Pipeline)的閥(ValveA)中。請求在Engine的管道中會傳遞到Engine Valve這個閥中。接着請求會從Engine Valve傳遞到一個Host的管道中,在該管道中傳遞到Host Valve這個閥裏。接着從Host Valve傳遞到一個Context的管道中,在該管道中傳遞到Context Valve中。接下來請求會傳遞到Wrapper C內的管道所包含的閥Wrapper Valve中,在這裏會經過一個過濾器鏈(Filter Chain),最終送到一個Servlet中。藉助於tomcat的這種架構設計,我們可以通過在tomcat處理一次請求的生命週期過程中植入自己的邏輯,將tomcat對外提供的能力進行一次增強,即UAV的中間件增強技術。

中間件增強技術除了巧妙運用了tomcat容器的架構設計之外還藉助了java Instrumentation(它給我們提供了一種能夠在對象第一次加載時動態修改字節碼的能力,由於篇幅原因在此不進行詳細講解,不明白的小夥伴自行查閱資料)。在UAV中通過UAVServer對外提供各種切點能力。

有了中間件增強技術,在應用邏輯執行之前和之後的切點就有了,接下來就是在這些切點位置執行我們自己的調用鏈邏輯了。

四、中間件增強技術在調用鏈中的使用

上文介紹的間件增強技術是一種通過使用javaagent方式動態地在tomcat代碼中植入切點代碼並以UAVServer的形式對外提供能力的框架(具體能力後續文章會詳細介紹)。輕調用鏈實現正是使用了UAVServer對外提供的GlobalFilterHandler能力。

GlobalFilterHandler: 這裏的GlobalFilterHandler是中間件增強技術中的一種能力,與傳統的filter沒有任何關係。它對外提供了四個能力:

  • doRequest:在所有應用處理請求之前進行劫持;
  • doResponse:在所有應用處理請求之後進行劫持;
  • BlockHandlerChain:阻塞自當前handler以後的所有handler,此處的handler爲註冊在當前;
  • BlockFilterChain阻塞自當前Filter以後的所有Filter。

調用鏈藉助於GlobalFilterHandler提供的前兩個能力,實現了在應用處理請求之前和之後執行調用鏈邏輯的功能。

五、輕調用鏈實現

具體UML圖如下:

從UML圖中可以清晰地看到, InvokeChainSupporter(調用鏈實現邏輯入口和調用鏈所需資源初始化實現類)將中間件增強技術進行了二次增強。它允許使用者在其中註冊不同的handler,並且在handler的preCap和doCap(中間件增強技術中的邏輯執行之前和之後的切點術語)方法之前和之後動態織入adapter,從而能夠執行更多的定製化適配和個性化邏輯。所有supporter和adapter均採用反射調用方式,最大程度上減少了中間件增強技術的依賴。

有了二次增強技術,我們就可以開始下面的調用鏈繪製工作了。

輕調用鏈繪製實現主要依賴於註冊在InvokeChainSupporter上的ServiceSpanInvokeChainHandler。主要繪製過程如下:

  • 解析請求信息,提取其中調用鏈關心的信息,並將解析出來的信息放入上下文中;
  • 通過解析出來的請求頭信息進行邏輯分流,根據不同的協議類型就行不同的邏輯處理;
    ✔mq邏輯
    ✔http邏輯
    ✔dubbo邏輯
  • 初始化調用鏈上下文,並初始化main span上下文;
  • 在應用處理完請求之後,將調用鏈信息進行統一輸出。

下面來看一下具體每一步都做了什麼。

5.1 解析請求信息

對於像tomcat這類中間件容器,所有進入tomcat的請求都會被封裝成HttpServletRequest和HttpServletResponse(後面簡稱request和response)最終進入用戶的servlet中。調用鏈藉助於中間件增強技術會在用戶邏輯處理之前將request和response進行一次攔截,並解析其中是否含有調用鏈信息。如果有則將調用鏈信息進行封裝放入上下文中。

5.2 邏輯分流

由於不同協議對應的調用鏈繪製邏輯也不同,此處調用鏈會根據協議類型進行一次分發。

5.3 初始化調用鏈上下文

將調用鏈上下文中的信息進行解析:

  • 沒有父節點則將當前節點當作初始化節點,並初始化記錄當前服務內調用鏈信息的main span;
  • 有父節點則根據父節點信息初始化當前節點,並初始化記錄當前服務內調用鏈信息的main span。

main span:在服務內可能會進行多次客戶端通訊或服務間通訊,需要一個main span來記錄當前服務內調用鏈最後一個節點的信息。

5.4 調用鏈信息輸出

在用戶邏輯處理結束之後,調用鏈記錄器會從上下文中取出當前服務的調用鏈信息並將其輸出到指定日誌路徑。

5.5 服務間上下文傳遞

對於不同協議調用鏈傳遞信息方式也略有不同,具體實現方式藉助了中間件增強技術提供的另一個能力:AppFrkHook(簡稱hook,此功能在客戶端調用鏈實現時會進行具體介紹)。它能夠對用戶使用的客戶端技術進行劫持,如用戶使用了httpclient進行通訊,則對httpclient進行劫持並動態織入代碼,從而達到在http通訊的過程中注入調用鏈上下文信息的效果。目標服務在解析請求信息時,將調用鏈上下文進行解析;在初始化調用鏈上下文邏輯時,使用傳遞過來的信息初始化目標服務的調用鏈上下文,實現跨系統調用時調用鏈連接。

六、總結

讀完本文之後讀者應該對中間件增強技術的實現有了一個大概的瞭解,並且對其提供的GlobalFilterHandler能力有了一定的認識。對於調用鏈應明白了服務端和服務間調用鏈的繪製全過程。

作者:李崇

來源:宜信技術學院

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章