上一节【RPC调用上下文的介绍】中建立了全链路调用的环境中必需的环境信息,例如应用名、调用的ip、等其他信息属性等。本节主要会建立相关的

特性说明

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

使用场景

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

上下文的种类

Dubbo3中,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 获取到。

链路调用中上下文的使用流程

dubbo rest 中使用 thymeleaf dubbo traceid_开发语言

@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();
}
  • 覆盖及实现PenetrateAttachmentSelector 的实现,可以在方法中RpcContext.getServerAttachment()调用获取参数,然后我们可以手动将此实现类过滤掉所有value为null的数据,所以通过此方法可以实现防止参数污染。
  • 返回值
    Map类型数据值,key:参数传递的key,value:参数传递作为数据的值。
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
public class DefaultPenetrateAttachmentSelector implements PenetrateAttachmentSelector {
    @Override
    public Map<String, Object> select() {
        String traceParameter = RpcContext.getServerAttachment().getAttachment("traceId");
        System.out.println(" parameter:"+traceParameter);
        return RpcContext.getServerAttachment().getObjectAttachments();
    }
}
其中属性说明
  • Constants.PROVIDER:代表着作用于服务提供者端
  • Constants.CONSUMER:代表着作用于服务消费者端
服务提供者
public class ContextServiceImpl implements ContextService {

    @Override
    public String context(String name) {
        RpcContext.getServerAttachment().setAttachment("traceId", UUID.randomUUID().toString());
        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress() +
                ", client: " + clientIP + ", local: " + application + ", remote: " + remoteApplication +
                ", isProviderSide: " + isProviderSide;
    }
}
服务消费者
RpcContext.getServerAttachment().setAttachment("traceId", UUID.randomUUID().toString());
展示效果

dubbo rest 中使用 thymeleaf dubbo traceid_dubbo_02

可以看到在作为服务提供这段还没有traceId,但是作为服务消费者的时候,由于Provider注入了traceId,所以就可以获取到了uuid。