調用鏈系列(4):服務信息上下文傳遞

      網友投稿 642 2025-04-01

      在之前的調用鏈系列文章中,我們已經對調用鏈進行了詳細介紹,相信大家已經對調用鏈技術有了基本的了解。

      拓展閱讀:

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

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

      調用鏈系列(3):如何從零開始捕獲body和header

      其實,在調用鏈的繪制過程中,調用鏈上下文的傳遞非常值得關注。各個節點在獲取上層上下文后生成新的上下文并向后傳遞。在傳遞過程中,上下文一旦丟失或出現異常就會導致調用鏈數據缺失,甚至可能會發生斷裂。

      本文主要講述UAV中調用鏈上下文傳遞過程中的部分實現細節。

      前言

      在調用鏈的實現中,主要存在以下幾種調用鏈上下文的傳遞方式:

      請求處理前到請求處理后的上下文傳遞;

      各個客戶端調用間的上下文傳遞;

      各個服務間調用時的上下文傳遞。

      在這三種情況中,上下文傳遞過程中所傳遞的信息以及遇到的問題會有所不同。

      在請求處理前后的上下文傳遞過程中,需要傳遞的信息一般包括traceID、 spanID、請求開始的時間以及部分請求參數等。相關代碼可能會因為異步執行導致上下文面臨異步線程傳遞的問題。

      在客戶端調用間及服務間調用中,需要傳遞的上下文信息一般只包括traceID和spanID。但客戶端調用之間的上下文傳遞可能會遇到跨線程池傳遞的問題,服務間調用則存在跨應用傳遞的問題。

      因此,我們把今天所講的上下文傳遞劃分為以下四種場景進行分析:

      在同一線程內傳遞

      跨線程池傳遞

      異步線程傳遞

      跨應用傳遞

      為了更好地闡述這四種場景,我們假設存在以下業務調用過程:

      假設某次請求首先進入服務A,在服務A的業務代碼中發起了一次JDBC請求,訪問了一次數據源;然后又通過httpClient(同步,異步)發起了一次http訪問并返回相應結果。

      數字表示所在點存在調用鏈上下文信息的獲取。在大多數的相鄰點之間都會涉及到調用鏈上下文的傳遞。

      例如,從2點到3點就是請求前和請求后的上下文傳遞,從3點到4點就是兩次客戶端調用間的上下文傳遞,從4點到5點就是服務間的上下文傳遞。下面我們將在不同的場景下說明各點之間的上下文傳遞過程。

      一、在同一線程內的上下文傳遞

      這種場景比較常見,也是最簡單的場景。

      假設上述模擬流程中全部為同步操作,業務代碼中不涉及任何的線程池(數據庫連接池不影響)及異步操作,那么服務A中調用鏈的相關代碼均會在同一個線程中執行。

      說到這里,想必大家都會想到使用ThreadLocal便可以解決。使用ThreadLocal的確可以解決同線程中的參數共享傳遞問題。在UAV中,一般兩次客戶端調用之間的上下文傳遞都直接使用ThreadLocal(其實并不是原生的ThreadLocal,后文會有所介紹),傳遞過程如下:

      但是很多時候,業務代碼中經常會涉及到異步或者提交線程池的操作,此時單單使用ThreadLocal便無法滿足相應的需求。下面我們就來討論有關含有線程池操作和異步請求的上下文傳遞問題。

      二、跨線程池的上下文傳遞

      首次我們來看一下跨線程池上下文傳遞問題。

      假設上述的業務場景中在進行JDBC操作時,當前的線程僅負責將JDBC操作提交到線程池中,那么此時上下文信息從1點傳遞到2點就會遇到跨線程池的問題,此時使用ThreadLocal無法上下文信息的傳遞。

      當然有的同學可能會說用InheritableThreadLocal。但是提交線程和線程池線程本身并不存在父子關系,因此InheritableThreadLocal也是無法完成跨線程池的上下文傳遞的。

      為了解決這個問題,我們使用了阿里開源的跨線程池的ThreadLocal組件:transmittable-thread-local(以下簡稱TTL,具體的實現方式有興趣的同學可以去了解下https://github.com/alibaba/transmittable-thread-local)。

      通過該組件可以增強ThreadLocal的功能實現跨線程池的傳遞。以下是github中TTL的使用示例:

      可以看到,想要TTL起作用,就需要將業務代碼中的runnable更換為TtlRunnable。為了實現對業務代碼的零入侵,我們借助javaagent機制增加了一個針對ThreadPoolExecutor等一些Eexecutor的ClassFileTransformer,將提交到線程池中的Runnable和Callable包裝成相應的TtlRunnable和TtlCallable,這樣就實現了在不修改業務代碼的情況下完成跨線程池的上下文傳遞。

      另外,由于TTL具備ThreadLocal的所有特性,因此UAV的上下文傳遞過程中用到的ThreadLocal均是TTL。

      三、異步線程中上下文傳遞

      看完上面的跨線程池操作,我們再來看一下異步線程的問題。

      假設在上述模擬場景中,我們使用異步HttpClient發送了一個異步的Http請求。由于是異步操作,4點的代碼和7點的代碼(這里7點的上下文是從4點中獲取的屬于請求前后的上下文獲取場景)實際上會在不同的線程中執行,導致7點無法獲取4點放入ThreadLocal中的上下文數據,進而導致調用鏈的數據丟失。

      為了解決這個問題,在UAV中我們同時使用了字節碼改寫和動態代理技術。關鍵在于目標jiechi函數的選擇,需要能夠獲取到異步線程的回調對象。

      下面以異步HttpClient為例介紹UAV中異步線程上下文的傳遞過程。

      在異步HttpClient中,我們jiechi的是InternalHttpAsyncClient類的execute()方法,該方法聲明如下:

      一般情況下,異步的使用方式為傳入一個callback接口對象,在callback中實現相應的異步邏輯;或者使用返回的Future接口對象的get()方法實現一種異步轉同步的操作。

      為了能夠在相應的地方獲取到調用鏈的上下文,我們首先通過改寫字節碼的方式,在方法執行前生成調用鏈的上下文信息;然后對FutureCallback接口做動態代理,同時將生成的上下文信息傳入到代理對象中,并替換原來的callback對象。

      這樣當異步請求返回調用callback接口時,實際上拿到的是我們的代理對象,此時也就完成了異步線程中上下文的傳遞過程,具體過程如下:

      為了支持通過get()方法的異步轉同步操作,在這里我們也對返回的Future接口做了動態代理來完成上下文的傳遞。

      四、跨應用上下文傳遞

      說完應用內的上下文傳遞過程,我們來看一下跨應用的上下文傳遞問題。

      跨應用的場景也是比較常見的。在這種場景下,上下文傳遞的思路一般是將上下文的信息按照一定的協議反序列化,然后放入到請求的傳輸報文中;在下游服務中jiechi請求,獲取其中的信息完成上下文的傳遞。在整個處理過程中,不對應用報文解析造成任何影響。

      常見的傳輸協議中如HTTP協議,Dubbo的RPC協議,RocketMQ的MQ協議等。這些協議一般會含有類似于頭信息的結構,用于表示本次請求的額外信息。

      我們恰好可以利用這一點,將上下文信息放入其中傳輸給下游服務,完成上下文的傳遞。

      下面我們仍然以異步HttpClient來介紹UAV跨應用上下文的傳遞過程。

      之前我們說過,在異步HttpClient中,我們jiechi的是execute()方法。在這個方法中,我們可以拿到HttpAsyncRequestProducer接口對象,具體接口如下:

      通過其中的generateRequest()方法,我們就可以拿到本次請求將要發送的request對象,利用request的setHeader()方法,將調用鏈的上下文信息放入Header中傳入下游。

      這里的上下文一般比較簡單,基本上都是由traceID和spanID的字符串構成,傳輸成本也不高。

      調用鏈系列(4):服務信息上下文傳遞

      至于下游服務中如何解析該上下文,實際上之前的調用鏈系列中有談到,就是借助UAV的中間件增強框架(MOF),在服務端jiechi請求對應的request對象,然后直接從其頭信息中獲取即可。

      其他的RPC或者MQ等協議,在UAV中均是采用這種方式完成,只是具體的API和jiechi點有所不同。

      例如,Dubbo遠程調用過程中使用是其中的RpcContext,而RocketMQ則是放入到了msg的UserProperty中。感興趣的同學可以到UAVStack(https://github.com/uavorg/uavstack)中查看相關的源碼。

      總結

      了解這些上下文的傳遞過程后,大家便可以基于調用鏈實現更為強大的功能。UAV中,調用鏈和日志關聯功能就是通過jiechi日志輸入部分的相關代碼,獲取調用鏈上下文,然后將traceID輸出到業務日志中來實現的。

      大家也可以自己在業務代碼中嘗試獲取調用鏈的上下文,將業務數據與調用鏈數據打通,方便數據統計和問題排查。

      本文轉載自異步社區。

      軟件開發 智能數據 Web應用防火墻 WAF

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel2010表格求值怎么做(excel表格如何求值)
      下一篇:Excel自選圖形的旋轉和翻轉
      相關文章
      久久亚洲2019中文字幕| 亚洲高清偷拍一区二区三区 | 亚洲乱码无码永久不卡在线| 亚洲国产精品尤物YW在线观看| 国产精品久久久久久亚洲影视| 亚洲人妖女同在线播放| 伊人久久综在合线亚洲2019| 亚洲黄色在线观看网站| 亚洲黄色网址在线观看| 亚洲午夜电影在线观看高清| 亚洲AV无码成人专区| 亚洲国产成a人v在线观看| 精品亚洲成A人无码成A在线观看| 亚洲一区电影在线观看| 中文字幕在线观看亚洲视频| jiz zz在亚洲| 亚洲AV无码专区国产乱码不卡| 免费亚洲视频在线观看| 亚洲国产av无码精品| 亚洲综合色区在线观看| 国产精一品亚洲二区在线播放| 亚洲av中文无码乱人伦在线r▽ | 久久99国产亚洲精品观看| 亚洲日本va午夜中文字幕一区| 亚洲黄色在线播放| 亚洲制服丝袜中文字幕| 亚洲欧美成人综合久久久| 成人亚洲国产精品久久| 亚洲日韩在线观看免费视频| 亚洲色精品88色婷婷七月丁香| 亚洲Av永久无码精品三区在线| 亚洲国产美国国产综合一区二区| 亚洲色图视频在线观看| 亚洲国产成a人v在线观看| 久久久久亚洲国产AV麻豆| 亚洲性日韩精品一区二区三区 | 亚洲va久久久噜噜噜久久狠狠| 亚洲精品国产情侣av在线| 亚洲人精品亚洲人成在线| 337P日本欧洲亚洲大胆艺术图| 亚洲婷婷国产精品电影人久久|