上一节【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 获取到。
链路调用中上下文的使用流程
@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());
展示效果
可以看到在作为服务提供这段还没有traceId,但是作为服务消费者的时候,由于Provider注入了traceId,所以就可以获取到了uuid。