上下文参数传递

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

上下文参数传递

在 Dubbo 3 中,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 获取到

使用场景

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

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

使用方式

setAttachment 设置的 KV 对,在完成下面一次远程调用会被清空,即多次远程调用要多次设置。

接口定义:

public interface ContextService {
    String invoke(String param);
}

服务实现:

@DubboService
public class ContextServiceImpl implements ContextService{
    @Override
    public String invoke(String param) {
        //ServerAttachment接收客户端传递过来的参数
        Map<String, Object> serverAttachments = RpcContext.getServerAttachment().getObjectAttachments();
        System.out.println("ContextService serverAttachments:" + JSON.toJSONString(serverAttachments));
        //往客户端传递参数
        RpcContext.getServerContext().setAttachment("serverKey","serverValue");
        StringBuilder s = new StringBuilder();
        s.append("ContextService param:").append(param);
        return s.toString();
    }
}

接口调用:

    //往服务端传递参数
    RpcContext.getClientAttachment().setAttachment("clientKey1","clientValue1");
    String res = contextService.invoke("context1");
    //接收传递回来参数
    Map<String, Object> clientAttachment = RpcContext.getServerContext().getObjectAttachments();
    System.out.println("ContextTask clientAttachment:" + JSON.toJSONString(clientAttachment));
    System.out.println("ContextService Return : " + res);

注意

path, group, version, dubbo, token, timeout 几个 key 是保留字段,请使用其它值。

历史遗留问题

在之前的版本中,你可能会见到这样的使用方式:

@Activate(group = {CommonConstants.CONSUMER})
public class DubboConsumerFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        
        RpcContext.getContext().setAttachment("demo","demo02");


        return invoker.invoke(invocation);
    }
}

但是在新版本中我们的建议是 在 Filter 里面的尽可能不要操作 RpcContext,上面的使用方式会导致不生效。原因在于新版本中,我们在ConsumerContextFilter类中做了ClientAttachment -> Invocation属性的复制,该类是Dubbo内置Filter类,而内置Filter类先于用户定义Filter类执行,所以在自定义Filter类中这样使用不会生效。 可以直接使用这种方式进行传递:

@Activate(group = {CommonConstants.CONSUMER})
public class DubboConsumerFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        
        invocation.setAttachment("demo","demo02");

        return invoker.invoke(invocation);
    }
}