Dubbo异步转同步

  Dubbo是一款开源的RPC中间件框架,底层数据传输默认使用的Netty,那么请求的处理理论上是异步的,为什么我们在使用的时候是同步的呢?肯定是Dubbo框架,做了异步转同步的处理。

  首先我们来梳理下,异步转同步,我们的需求是怎样的?

  1、调用方请求远程服务之后,需要等待结果,此刻,请求线程应该阻塞

  2、远程服务返回结果后,唤醒请求线程,调用方得到结果

  Dubbo异步转同步,核心类是DefaultFuture,核心方法是get(),received(Channel channel, Response response)。

  DefaultFuture构造函数:

 1     private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
 2 
 3    // 每次请求都会生成一个DefaultFuture对象,然后保存到FUTURES中,请求返回结果时,根据id从FUTURES中找到对应的DefaultFuture对象,并删除
 4     private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
 5 
 6     // AtomicLong从0开始递增,创建Request对象时生成的id
 7     private final long id;
 8     private final Channel channel;
 9     // 请求对象
10     private final Request request;
11     // 超时的设置
12     private final int timeout;
13     // 这里使用Lock和Condition实现等待通知机制
14     private final Lock lock = new ReentrantLock();
15     private final Condition done = lock.newCondition();
16     private final long start = System.currentTimeMillis();
17     private volatile long sent;
18     // 请求的返回结果
19     private volatile Response response;
20     private volatile ResponseCallback callback;
21 
22     public DefaultFuture(Channel channel, Request request, int timeout) {
23         this.channel = channel;
24         this.request = request;
25         this.id = request.getId();
26         this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
27         // put into waiting map.
28         FUTURES.put(id, this);
29         CHANNELS.put(id, channel);
30     }

 

  get():

 1 public Object get(int timeout) throws RemotingException {
 2         if (timeout <= 0) {
 3             timeout = Constants.DEFAULT_TIMEOUT;
 4         }
 5         // isDone()方法就是判断Response是否有值(即是否有返回结果)
 6         if (!isDone()) {
 7             long start = System.currentTimeMillis();
 8             lock.lock();
 9             try {
10                 while (!isDone()) {
11                     // 超时等待
12                     done.await(timeout, TimeUnit.MILLISECONDS);
13                     // 如果有返回结果了,或者,超时了,就退出循环
14                     if (isDone() || System.currentTimeMillis() - start > timeout) {
15                         break;
16                     }
17                 }
18             } catch (InterruptedException e) {
19                 throw new RuntimeException(e);
20             } finally {
21                 lock.unlock();
22             }
23             // 如果是超时了,就抛出异常
24             if (!isDone()) {
25                 throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
26             }
27         }
28         // 远程服务正常返回结果,则返回给调用方
29         return returnFromResponse();
30     }

 

  received(Channel channel, Response response):

 1 public static void received(Channel channel, Response response) {
 2         try {
 3             // 根据请求id从FUTURES中获取DefaultFuture,并删除
 4             DefaultFuture future = FUTURES.remove(response.getId());
 5             if (future != null) {
 6                 future.doReceived(response);
 7             } else {
 8                 logger.warn("The timeout response finally returned at "
 9                         + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
10                         + ", response " + response
11                         + (channel == null ? "" : ", channel: " + channel.getLocalAddress()
12                         + " -> " + channel.getRemoteAddress()));
13             }
14         } finally {
15             // CHANNELS也删除
16             CHANNELS.remove(response.getId());
17         }
18     }
 1 private void doReceived(Response res) {
 2         lock.lock();
 3         try {
 4             response = res;
 5             if (done != null) {
 6                 // 唤醒阻塞的线程
 7                 done.signal();
 8             }
 9         } finally {
10             lock.unlock();
11         }
12         if (callback != null) {
13             invokeCallback(callback);
14         }
15     }

 

  总结:Dubbo异步转同步的原理,其实就是利用Lock和Condition实现了等待通知机制。请求与返回结果进行匹配,则是通过传递以及接收请求id实现的。

posted @ 2020-02-26 23:41  嘟神子  阅读(3010)  评论(0编辑  收藏  举报