RpcContext
大家知道RpcContext是记录一次调用的上下文信息。源码中的注释如下(RpcContext是一个临时状态持有者。每次发送或接收请求时,RpcContext中的状态都会更改)
/**
* Thread local context. (API, ThreadLocal, ThreadSafe)
* Note: RpcContext is a temporary state holder. States in RpcContext changes every time when request is sent or received.(RpcContext是一个临时状态持有者。每次发送或接收请求时,RpcContext中的状态都会更改)
* For example: A invokes B, then B invokes C. On service B, RpcContext saves invocation info from A to B before B
* starts invoking C, and saves invocation info from B to C after B invokes C.
*/
是通过Threadlocal实现的,不过这里的Threadlocal不是JDK中的Threadlocal,而是经过优化过,在Dubbo中叫InternalThreadLocal。
/**
* InternalThreadLocal
* A special variant of {@link ThreadLocal} that yields higher access performance when accessed from a {@link InternalThread}.
(当从InternalThread访问时,可产生更高的访问性能。)
* Internally, a {@link InternalThread} uses a constant index in an array, instead of using hash code and hash table,
* to look for a variable. Although seemingly very subtle, it yields slight performance advantage over using a hash
* table, and it is useful when accessed frequently.
* This design is learning from {@see io.netty.util.concurrent.FastThreadLocal} which is in Netty.
*/
public class InternalThreadLocal
可以看到设计思想来自netty中的FastThreadLocal。主要是通过空间换时间,在ThreadLocal中如果有hash碰撞,是通过线性搜索解决的,使得获取时不能达到完美的O(1),InternalThreadLocal在数组中使用常量索引,而不是使用哈希码和哈希表来查找变量。尽管看似非常微妙,但与使用hash表相比,它在性能上却有一点优势,并且在经常访问时很有用。
Threadlocal最终还是跟Thread内中的ThreadlocalMap挂钩的,因此这一块的设计在InternalThreadLocal中是相同。
image
在Dubbo业务线程池创建调用时也是使用线程工厂创建InternalThread来支持后续InternalThreadLocal的使用。
image
Threadlocal内部模型
image
这个是Threadlocal中的使用流程。通过hashcode+线性探测法。
InternalThreadlocal内部模型
image
而在InternalThreadLocal的内部模型则如上图所示,通过常量索引来进行优化。在大幅使用上可以进行一部分的性能优化。
源码分析
初始化时进行索引分配
public class InternalThreadLocal<V> {
private final int index;
public InternalThreadLocal() {
//初始化时,获取常量索引
index = InternalThreadLocalMap.nextVariableIndex();
}
}
InternalThreadLocalMap中的分配index方法。
public final class InternalThreadLocalMap {
//发号器
private static final AtomicInteger NEXT_INDEX = new AtomicInteger();
public static int nextVariableIndex() {
int index = NEXT_INDEX.getAndIncrement();
//说明超过Int的最大值了
if (index < 0) {
NEXT_INDEX.decrementAndGet();
throw new IllegalStateException("Too many thread-local indexed variables");
}
return index;
}
}
InternalThreadLocal中的get方法,结合当前的index直接获取相应的值。
public final V get() {
//获取当前InternalThread中的map
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
//根据索引直接获取值,O(1)
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
写的有错的地方或者不好的地方,欢迎大家提出意见或者建议!(鞠躬