通过调用链接传递隐式参数
功能描述
通过 RpcContext
上的 setAttachment
和 getAttachment
,可以在服务消费者和提供者之间隐式传递参数。
背景
上下文信息是 RPC 框架中非常重要的功能。使用 RpcContext 可以为单个调用指定不同的配置。例如,在分布式链路追踪场景中,实现原理是在整个链路的上下文中维护一个 traceId。Consumer 和 Provider 通过传递 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 内部使用,用于在调用链路上传递参数信息,例如 invoker 对象等。
- 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 几个键是保留字段,请使用其他值。
使用场景
当内部系统通过 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();
}