Dubbo之RpcContext原理

1.前言 【Dubbo之RpcContext原理】最近在开发一个消息通知功能,需要将每个功能的dubbo rpc 接口参数通过MQ发送,消费方消费MQ将参数转化成消息通知的必要数据存数据库,用户通过接口获取自己的相关消息.依赖的RPC接口在设计上并没有操作者的参数,需求需要记录操作者.这里有个思路就是利用RpcContext,在一次完整的RPC调用链路中,将需要的参数透传过去.下面讲下使用RpcContext的使用原理以及使用RpcContext所走的坑.
2.上下文信息 RpcContext本质上是一个ThreadLocal,当接收到RPC请求或发起RPC请求时,RpcContext的状态会变化。比如A调用B,B再调用C,则B机器上,在B调用C之前,RpcContext记录的是A调用B的信息,在B调用C之后,RpcContext记录的是B调用C.
3.RpcContext的使用

//服务提供方使用,获取参数 RpcContext.getContext().getAttachments() //服务器消费方使用,设置参数 RpcContext.getContext().setAttachment() //调接口时,必须是A直接到B,如果A没有直接到B,而是先到C,再由C到B,那么在B里getAttachment()获取不到值

4 结合dubbo拦截器使用RpcContext 使用dubbo拦截器的原因是主要有两个:
1) 统一入口设置参数,节省代码方便维护
2) 解决一次完整请求调用涉及多次rpc调用时获取不到上下文中设置的参数值.
在一次线程上下文使用拦截器的例子
public class TraceIdFilter implements Filter { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { String traceId = RpcContext.getContext().getAttachment("BizId"); if ( !StringUtils.isEmpty(BizId) ) { //从RpcContext里获取bizId并保存 BizIdUtils.setBizId(bizId); } else { //交互前重新设置traceId, 避免信息丢失 RpcContext.getContext().setAttachment("bizId", BizIdUtils.getBizId()); } //实际的rpc调用 return invoker.invoke(invocation); } } ? public class BizIdUtils { private static final ThreadLocal Cache = new ThreadLocal(); public static String getBizId() { return Cache.get(); } public static void setBizId(String bizId) { Cache.set(bizId); } public static void clear() { Cache.remove(); } }

如果拦截的方法使用了异步处理,同个线程上下文下可能获取不到值,这时候dubbo拦截器获取值设置进上下文可能是空的,这一点需要注意.
5.RpcContext原理 RpcContext内部有一个ThreadLocal变量,它是作为ThreadLocalMap的key,表明每个线程有一个RpcContext
public class RpcContext { private static final ThreadLocal LOCAL = new ThreadLocal() { @Override protected RpcContext initialValue() { return new RpcContext(); } }; ? //如果get在set之前,则get会返回initialValue()创建的对象 public static RpcContext getContext() { return LOCAL.get(); } }

    推荐阅读