通过调用链接传递隐式参数

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

功能描述

通过 RpcContext 上的 setAttachmentgetAttachment,可以在服务消费者和提供者之间隐式传递参数。

背景

上下文信息是 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 中获取

/imgs/v3/concepts/rpccontext.png

如上图所示,当消费者发起调用时,它可以通过 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();

}

上次修改时间:2023 年 1 月 2 日:增强英文文档 (#1798) (95a9f4f6c1c)