两个微服务之间通过dubbo调用时,除了通过在方法中添加参数传递变量之外,如果有些公用变量,如用户session的信息,那么可以通过RpcContext来调用,今天我们来聊一下RpcContext的工作原理。
首先看RpcContext中的一段代码:
public class RpcContext {
private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
@Override
protected RpcContext initialValue() {
return new RpcContext();
}
};
//此处省略很多行
......
......
}
项目启动后,因为是静态变量,这个ThreadLocal<RpcContext> 对象就创建了,并且重写了ThreadLocal 中的 initialValue() 方法。initialValue() 的调用会重新创建一个 RpcContext 对象。
传递变量时我们一般是这样使用的:
//存
RpcContext.getContext().setAttachment("user",data);
//取
RpcContext.getContext().getAttachment("user");
代码执行时首先调用getContext(),跟一下源码:
/**
* get context.
*
* @return context
*/
public static RpcContext getContext() {
return LOCAL.get();
}
发现是从 LOCAL 也就是上面静态对象 ThreadLocal<RpcContext> 中调用 get() 方法。
然后再跟踪 get() 方法,发现调用到 ThreadLocal 中的 get() 方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
从上面代码分析可以看出,如果 ThreadLocal 中存在当前线程为key的变量值,那么会返回取到的变量,如果取不到,则调用 setInitialValue() 方法:
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
发现这个方法第一行调用了 initialValue() 方法,则就是上面静态对象中重写的 initialValue() 方法,这个方法会返回一个新的 RpcContext 对象。
分析到这一步,那么我们就要看传递变量时,我们在上游服务set值,在下游服务get值,也就是:
//存
RpcContext.getContext().setAttachment("user",data);
//取
RpcContext.getContext().getAttachment("user");
setAttachment(key,value),getContext我们刚才已经分析了,本地线程变量ThreadLocal中有RpcContext对象的话,直接返回 ,没有的话返回一个新的,上游服务中第一次setAttachment时,是没有的,直接返回一个新的RpcContext对象,然后调用这个对象的setAttachment(key,value)这个方法,那么我们看一下这个方法:
private final Map<String, String> attachments = new HashMap<String, String>();
/**
* set attachment.
*
* @param key
* @param value
* @return context
*/
public RpcContext setAttachment(String key, String value) {
if (value == null) {
attachments.remove(key);
} else {
attachments.put(key, value);
}
return this;
}
可以看出,其实就是把 key value 存放到 RpcContext 中的一个map 对象中,一定要记得这个 RpcContext 对象是存放在 ThreadLocal 线程本地变量中的那个 RpcContext 对象的。
然后在下游服务通过 RpcContext.getContext().getAttachment(key);取的时候,我们看一下源码:
private final Map<String, String> attachments = new HashMap<String, String>();
/**
* get attachment.
*
* @param key
* @return attachment
*/
public String getAttachment(String key) {
return attachments.get(key);
}
可以看出,其实就是从 ThreadLocal 中存放的那个 RpcContext 对象中的 一个 map 对象中取的,上游服务如果存放了,那么只要是在同一个线程中,下游服务根据 key 就一定能获取到。