在 dubbo 中使用 ThreadLocal
在 Dubbo 中使用 ThreadLocal ,如果采用默认的设置,每次 Dubbo 调用结束,Dubbo 处理响应线程并不会被销毁, 而是归还到线程池中。而从 ThreadLocal 源码可以看出,每次我们设置的值其实会存在位于 Thread 中 ThreadLocalMap 变量中。
这就导致,下次如果 Dubbo 处理响应恰好继续使用到这个线程,该线程就能调用到上次响应中设置在 ThreadLocal 设置的值。这就引起内存泄露,可能还会导致业务上异常。其实并不止在 Dubbo 中,该案例还会发生在 web项目中,只要相关使用线程池的,都有可能发生。
dubbo 线程模型
dubbo的事件派发策略和线程池
dubbo基于netty----有5种派发策略:
默认是 all:所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。 即worker线程接收到事件后,将该事件提交到业务线程池中,自己再去处理其他事。
direct:worker线程接收到事件后,由worker执行到底。
message:只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO线程上执行
execution:只请求消息派发到线程池,不含响应(客户端线程池),响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行
connection:在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
dubbo 默认采用单一长连接加线程池方式处理调用。
默认采取 Dispatcher=all 的分发策略,所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
线程池在缺省配置为固定大小线程池,启动时建立线程,不关闭,一直持有。
业务线程池:
- fixed:固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
coresize:200
maxsize:200
队列:SynchronousQueue
回绝策略:AbortPolicyWithReport - 打印线程信息jstack,之后抛出异常 - cached:缓存线程池,空闲一分钟自动删除,需要时重建。
- limited:可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。
Threadlocal 总结
使用 Threadlocal,我们需要注意几点:
- 使用 Threadlocal 需要跟使用相关流一样,需要最后显示调用其 remove 方法,清除其设置的值,防止引起内存泄露。
- 不能什么传参都使用 Threadlocal 在方法中上下传递,这样就会引起隐形耦合,而且相关代码并不好维护。
- 高并发中,我们可以使用 Threadlocal 代替同步锁,提高相关性能。
dubbo 默认线程池大小是200
调整线程池大小配置是
dubbo.protocol.threads = 5000
调整线程池类型配置是
dubbo.protocol.threadpool = cached
调整事件处理方式配置是
dubbo.protocol.dispatcher = message
或者
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />