调用链路传递隐式参数

通过 Dubbo 中的 Attachment 在服务消费方和提供方之间隐式传递参数

在不修改方法签名与参数定义的情况下,可以通过 RpcContext 上的 setAttachmentgetAttachment 在服务消费方和提供方之间进行参数的隐式传递。隐式参数传递支持以下两个方向:

  • 从消费方到提供方,也就是在请求发起时,在方法参数之外通过 attachment 传递附加参数。
  • 从提供方到消费方,也就是在响应结果返回时,在响应结果之外通过 attachment 传递附加参数。

理解隐式参数传递的最直接方式 http header,它的工作方式与 http header 完全一致,在 GET 或 POST 请求体之外可以传递任意多个 header 参数。在实现原理上,对于不同的协议,attachment 的实现方式略有不同:

  • 对于 triple 协议,attachment 会转换为标准的 http header 进行传输。
  • 对于 dubbo 协议,attachment 是编码在协议体的固定位置进行传输,具体请参见 dubbo 协议规范。

/user-guide/images/context.png

消费端隐式参数

本文示例完整源码可在以下链接查看 dubbo-samples-attachment

设置隐式参数

RpcContext.getClientAttachment().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,比如用于框架集成
xxxService.xxx(); // 远程调用
// ...

读取隐式参数

public class XxxServiceImpl implements XxxService {

    public void xxx() {
        // 获取客户端隐式传入的参数,比如用于框架集成
        String index = RpcContext.getServerAttachment().getAttachment("index");
    }
}

提供端隐式参数

设置隐式参数

public class XxxServiceImpl implements XxxService {

    public void xxx() {
        String index = xxx;
        RpcContext.getServerContext().setAttachment("result", index);
    }
}

读取隐式参数

xxxService.xxx(); // 远程调用
String result = RpcContext.getServerContext().getAttachment("result");
// ...

内部实现原理

上下文信息是 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 的信息。

因此,RpcContext 被拆分为四大模块(ServerContext、ClientAttachment、ServerAttachment 和 ServiceContext)。

它们分别承担了不同的职责:

  • ServiceContext:在 Dubbo 内部使用,用于传递调用链路上的参数信息,如 invoker 对象等
  • ClientAttachment:在 Client 端使用,往 ClientAttachment 中写入的参数将被传递到 Server 端
  • ServerAttachment:在 Server 端使用,从 ServerAttachment 中读取的参数是从 Client 中传递过来的
  • ServerContext:在 Client 端和 Server 端使用,用于从 Server 端回传 Client 端使用,Server 端写入到 ServerContext 的参数在调用结束后可以在 Client 端的 ServerContext 获取到

/imgs/v3/concepts/rpccontext.png

如上图所示,消费端发起调用的时候可以直接通过 Method Invoke 向远程的服务发起调用,同时消费端往 RpcClientAttachment 写入的数据会连同 Invoke 的参数信息写入到 Invocation 中。 消费端的 Invocation 经过序列化后通过网络传输发送给服务端,服务端解析 Invocation 生成 Method Invoke 的参数和 RpcServerAttachment,然后发起真实调用。 在服务端处理结束之后,Method Response 结果会连同 RpcServiceContext 一起生成 Result 对象。 服务端的 Result 结果对象经过序列化后通过网络传输发送回消费端,消费端解析 Result 生成 Method Response 结果和 RpcServiceContext,返回真实调用结果和上下文给消费端。

1、Dubbo系统间调用时,想传递一些通用参数,可通过Dubbo提供的扩展如Filter等实现统一的参数传递

2、Dubbo系统间调用时,想传递接口定义之外的参数,可在调用接口前使用setAttachment传递参数。

最后修改 September 13, 2024: Refactor website structure (#2860) (1a4b998f54b)