dubbo

dubbo的一些分析和优化

dubbo应用线程分析

dubbo-remoting-client-heartbeat-thread

dubbo客户端心跳线程

线程启动类:HeaderExchangeClient

任务执行类:HeartBeatTask

此任务会定时向服务端发送心跳消息,每个连接一个任务,执行周期默认为60s,执行时会判断此Channel是否在心跳周期类有读写,如果没有,给服务端发送心跳信号.

改进措施:可以给执行线程合理命名。

xxxx-EventThread

zookeeper事件处理线程

线程启动类:org.apache.zookeeper.ClientCnxn

任务执行类:org.apache.zookeeper.ClientCnxn.EventThread

此线程的命名格式为启动线程名+EventThread,其中的localhost-startStop-1tomcat启动线程名。

CuratorFrameworkImpl启动时,会向zookeeper服务器建立连接,此时会创建此线程。

我们必须保证一个应用只有一个CuratorFrameworkImpl实例。

localhost-startStop-1-SendThread

zookeeper 心跳、发送请求线程

线程启动类:org.apache.zookeeper.ClientCnxn

任务执行类:org.apache.zookeeper.ClientCnxn.SendThread

DubboClientReconnectTimer-thread

消费者连接服务提供者的定时重连任务,默认执行周期2s,检查是否连接,没有连接重新连接。

线程启动类:com.alibaba.dubbo.remoting.transport.AbstractClient

任务执行类:com.alibaba.dubbo.remoting.transport.AbstractClient$1

DubboServerHandler-

dubbo服务端,任务处理线程,处理客户端请求

线程池启动类:AllChannelHandler的构造器中初始化线程池

线程池实际创建类:FixedThreadPool

任务处理类:com.alibaba.dubbo.remoting.transport.DecodeHandler

DubboClientHandler-

此线程主要用于dubbo客户端处理服务端的响应/连接相关事件,netty在接受到消息后,交给此线程池来处理。

线程启动类:com.alibaba.dubbo.remoting.transport.AbstractClient#wrapChannelHandler

线程池实际创建类:CachedThreadPool

任务处理类:com.alibaba.dubbo.remoting.transport.DecodeHandler

DubboRegistryFailedRetryTimer

当dubbo和注册中心的相关请求(注册、取消注册、订阅、取消订阅)处理失败时,会暂时放在缓存中,此定时任务会周期性的来处理这些失败的请求

线程启动类:com.alibaba.dubbo.registry.support.FailbackRegistry

处理任务:com.alibaba.dubbo.registry.support.FailbackRegistry#retry

DubboSaveRegistryCache

异步保存服务提供者地址

线程启动类:com.alibaba.dubbo.registry.support.AbstractRegistry

任务执行类:com.alibaba.dubbo.registry.support.AbstractRegistry.SaveProperties

DubboResponseTimeoutScanTimer

扫描等待返回的DefaultFuture对象,如果DefaultFuture超时,则抛出超时异常。

任务类:DefaultFuture.RemotingInvocationTimeoutScan

优化意见:目前的方式是间隔30ms就去扫描一次,建议重写ConcurrentHashMap缓存,加入队列等待机制。

telnet

com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable处理netty的事件,他会把telnet请求(decode后为文本)代理给com.alibaba.dubbo.remoting.telnet.support.TelnetHandlerAdapter来处理。

dubbo线程池任务不均衡问题分析

dubbo应用使用的线程池为com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool,如果当queue设置为0时,会使用SynchronousQueue,这个东东导致了任务线程执行”不均衡”(满足了大家的心理预期,其实这种不均衡方式减少了上下文切换,但是SynchronousQueue没有大小,不能起到任务缓冲的作用).

请在dubbo:protocol上加上queues大小(参考tomcat默认配置).

<dubbo:protocol name="dubbo" port="${dubbo.provider.port}" threads="200" queues="100"/>

测试:

修改前:

grep DubboServerHandler dubbo-demo.log |awk  -F '-'  '{print $6}' |awk  -F ']'  '{print $1}' |sort -n |uniq -c

  1 150
  1 168
  1 169
  1 170
  ...
  117 171
  5386 172
  714 173
      2646 174

修改后:

grep DubboServerHandler dubbo-demo.log |awk  -F '-'  '{print $6}' |awk  -F ']'  '{print $1}' |sort -n |uniq -c
507 1
498 2
 ...
493 199
489 200

线程池优化

jdk默认线程池实现策略如下:

  1. 当线程数少于corePoolSize,总是新建线程
  2. 当线程数=corePoolSize时,所有线程都忙,就会把线程放入队列;当队列满时,才会继续创建线程
  3. 当线程数量=maxPoolSize并且队列也满时,RejectedExecutionHandler生效。

易极付内部对线程池做了些优化:

  1. 我们希望能充分利用资源,仅当线程数量=maxPoolSize时,才放入队列。当线程空闲时,线程池收缩到corePoolSize。
  2. 当队列设置得比较小的时候,即便线程数量<maxPoolSize,在高并发下也会触发RejectedExecutionHandler
  3. 线程池没有传递MDC或者应用请求gid

对于1,2,可以参考tomcat内部的线程池实现org.apache.tomcat.util.threads.ThreadPoolExecutororg.apache.tomcat.util.threads.TaskQueue来改造。

对于3,wrap原任务即可,大致代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static class MDCGidCallable<T> implements Callable<T> {
private final Callable<T> task;
private final String gid;

public MDCGidCallable(Callable<T> task, String gid) {
this.task = task;
this.gid = gid;
}

@Override
public T call() throws Exception {
try {
MDC.put(GID_KEY, gid);
return task.call();
} finally {
MDC.remove(GID_KEY);
}
}
}

缓存扩展

下面是我们自己实现的cache机制,个人感觉比dubbo原生的清爽,通过dubbo filter实现。

源代码见dubbo-cache

blog 参考@DubboCache

dubbo mock

mock最好是有mock server。由于懒,把mock server的client实现了(拦截请求,转换为http+json调用到mock server),后面就没时间做mock server了。前段时间有个项目紧急需要,做了个简单的mock。

原理如下:

对于使用者:

  1. 用户配置需要mock的dubbo服务。

    比如: dubbo.consumer.mockInterfaces[0]=com.acooly.core.test.dubbo.mock.XXFacade

  2. 增加mock实现。

1
2
3
4
5
6
7
@Service
public class XXFacadeMock implements XXFacade {
@Override
public SingleResult<String> echo(SingleOrder<String> msg) {
return SingleResult.from("mocked");
}
}

组件提供的能力

  1. 自定义实现BeanPostProcessor,扫描所有标注@Reference注解的属性,如果被配置了要mock掉,设置属性为mock实现。

最后

最后附带一个在易极付写的dubbo分享。

给攻城狮一个小小的鼓励!