調用鏈傳遞隱式參數
功能描述
參數可以通过 RpcContext 上的 setAttachment 和 getAttachment 在服務消費者和提供者之間隱式傳遞。
背景
上下文信息是 RPC 框架中非常重要的功能。使用 RpcContext 可以為單次調用指定不同的配置。例如,在分佈式鏈路追蹤場景中,實現原理是在整個鏈路的上下文中維護一個 traceId。消費者和提供者通過傳遞 traceId 來連接 RPC 調用。在分別上報日誌後,它們可以在追蹤系統中串聯起來,並顯示完整的調用流程。這樣,更容易發現異常並定位問題。Dubbo 中的 RpcContext 是一個 ThreadLocal 臨時狀態記錄器。當收到 RPC 請求或發起 RPC 請求時,RpcContext 的狀態會發生變化。例如:**A 調用 B,B 調用 C,則在機器 B 上,B 調用 C 之前,RpcContext 記錄 A 和 B 的信息,B 調用 C 之後,RpcContext 記錄 B 和 C 的信息。**
在 Dubbo 3 中,RpcContext 被拆分為四大模組(ServerContext、ClientAttachment、ServerAttachment 和 ServiceContext)。
它們分別承擔不同的指責
- ServiceContext:Dubbo 內部使用,用於傳遞調用鏈路上的參數信息,例如調用器對象等。
- ClientAttachment:在客戶端使用,寫入 ClientAttachment 的參數將被傳遞到服務器端
- ServerAttachment:在服務器端使用,從 ServerAttachment 讀取的參數是從客戶端傳遞過來的
- ServerContext:在客戶端和服務器端都使用,用於從服務器端傳回給客戶端。服務器端寫入 ServerContext 的參數可以在調用結束後從客戶端的 ServerContext 中獲取
如上圖所示,當消費者發起調用時,它可以直接通過 Method Invoke 發起對遠端服務的調用,同時,消費者寫入 RpcClientAttachment 的數據會與 Invoke 的參數信息一起寫入 Invocation。消費者端的 Invocation 經過序列化後通過網絡傳輸發送至服務器端。服務器端解析 Invocation 生成 Method Invoke 的參數和 RpcServerAttachment,然後發起真正的調用。服務器端處理完成後,Method Response 結果會與 RpcServiceContext 一起生成 Result 對象。服務器端的 Result 對象經過序列化後通過網絡傳輸返回給消費者。消費者解析 Result 生成 Method Response 結果和 RpcServiceContext,並將實際的調用結果和上下文返回給消費者。
path、group、version、dubbo、token、timeout 這幾個 key 是保留字段,請使用其他值。
使用場景
如何在內部系統通過 Dubbo 調用時,透明地傳輸 traceId 給服務提供者。
如何使用
通過
setAttachment
設置的 KV 對,會在下一次遠程調用完成後清除,即多次遠程調用需要設置多次。
在服務消費端設置隱式參數
RpcContext.getClientAttachment().setAttachment("index", "1"); // Implicit parameter passing, subsequent remote calls will implicitly send these parameters to the server, similar to cookies, used for framework integration, not recommended business use
xxxService.xxx(); // remote call
//...
在服務提供端獲取隱式參數
public class XxxServiceImpl implements XxxService {
public void xxx() {
// Obtain the parameters implicitly passed in by the client for framework integration, not recommended for general business use
String index = RpcContext.getServerAttachment().getAttachment("index");
}
}
在服務提供端寫入返回參數
public class XxxServiceImpl implements XxxService {
public void xxx() {
String index = xxx;
RpcContext.getServerContext().setAttachment("result", index);
}
}
在消費端獲取返回參數
xxxService.xxx(); // remote call
String result = RpcContext.getServerContext().getAttachment("result");
//...
參數透明傳輸問題
在 Dubbo 2.7 中,A 端設置的參數調用後,如果 B 繼續調用 C,則 A 中原來設置的參數也會被帶到 C 端,造成參數污染問題。Dubbo 3 重構了 RpcContext 以支持可選的參數透明傳輸,默認開啟參數透明傳輸。
Dubbo 3 中提供了以下 SPI,默認沒有實現,用戶可以自定義實現,select
的結果(可以從 RpcClientAttachment 中獲取所有當前參數)會作為需要透明傳輸的鍵值對傳遞到下一跳,如果返回 null 則表示不進行參數透明傳輸。
@SPI
public interface PenetrateAttachmentSelector {
/**
* Select some attachments to pass to next hop.
* These attachments can fetch from {@link RpcContext#getServerAttachment()} or user defined.
*
* @return attachment pass to next hop
*/
Map<String, Object> select();
}