标号为 ① 的地方就是根据 response 里面的 id,即调用编号从 FUTURES 这个 MAP 中移除并获取出对应的请求。
如果获取到的请求是 null,说明超时了。
如果获取到的请求不为 null,则判断是否超时了。超时逻辑我们最后再讲。
标号为 ② 地方是要把响应返回给对应的用户线程了。
在 doReceived 里面使用了响应式编程:
这的 this 就是当前类,即 DefaultFuture。
那么这个 doReceived 方法是怎么调到这里的呢?
之前的文章说过 Dubbo 默认的派发策略是 ALL,所以所有的响应都会被派发到客户端线程池里面去,也就是这个地方:
当接收到服务端的响应后,响应事件也会被扔到线程池里面,从代码中可以看到,扔进去的就是一个 Runable 任务。
然后执行了 execute 方法,这个方法就和上一小节讲请求的地方呼应上了。
还记得我们的请求是调用了 queue.take 方法,进入阻塞等待吗?
而这里就是在往 queue 里面添加任务。
队列里面有任务啦!在阻塞等待的用户线程就活过来了!
接下来用户线程怎么执行?
看代码:
取到任务后执行了任务的 run 方法。注意是 run 方法哦,并不会起新的线程。
而这个任务是什么任务?
上周的文章也说到了这个方法。
而 handler.received 方法最终就会调用到我们前说的 doReceived 方法:
这里返回的 Result 就是最终的服务端返回的数据了,或者是返回的异常。
现在你再回过头去看官网这张图,应该就能看明白了:
超时检查
前面说 newFuture 的时候不是说它还干了一件事就是检测是否超时嘛。其实原理也是很简单:
首先有一个 TimeoutCheckTask 类,这是一个待执行的任务。
触发后会根据调用编号去 FUTURES 里面取 DefaultFuture。
前面我刚刚说了:如果一个 future 正常完成之后,会从 FUTURES 里面移除掉。
那么如果到点了,根据编号没有取到 Future 或者取到的这个 Future 的状态是 done 了,则说明这个请求没有超时。
如果这个 Future 还在 FUTURES 里面,含义就是到点了你咋还在里面呢?那肯定是超时了,调用 notifyTimeout 方法,是否超时参数给 true: