本篇主要介绍一下dubbo 2.7.x以及3.0 版本是如何实现netty异步转同步的操作的,我们知道netty的请求是异步回调的,那怎么实现同步获取结果呢?看完本篇,对于使用的netty的同学也会有一些帮助。
知识点
- dubbo 2.7.x如何实现异步转同步
- dubbo 3.0版本是如何实现的
dubbo 2.7.x如何实现异步转同步
源码我们直接从发请求这一段开始看 org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
红框处就是同步请求逻辑,继续进去看下请求是怎么发的 org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#request(java.lang.Object, int)
这里是非常重要的一段,我们看到有一个 DefaultFuture,这里的 newFuture 就是新建一个DefaultFuture,进入 DefaultFuture 的构造函数
可以看到他把本次请求的id和对应的channel以及自身关联起来了,这里我们就可以联想到在响应之后从这里面根据id取出来就可以了。FUTURES 是一个线程安全的Map
再回到第一幅图的逻辑 (Result) currentClient.request(inv, timeout).get();
处,这里的 get 就是一个同步等待结果的逻辑(设置了超时时间)
进去get方法之后可以看到其实就是使用了一个对象锁等待信号量释放或者超时,这里我们又会联想到信号量在哪里释放,肯定是收到响应的逻辑里。我们在netty初始化的时候设置了channelhandler,细节就不讲了,直接看代码处 org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleResponse
这段代码可以看到收到响应后又用 DefaultFuture 进行了处理,我们看看是否和我们上面预期的处理一样
这里看到把响应结果设置到变量 response 之后触发对象信号量释放,和我们预想的一样。这里要注意的是信号量等待和释放必须要在同步块里,所以需要加lock,当然也可以用synchronized。
dubbo 3.0 版本如何实现
还是这段逻辑 org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
可以看到这段代码做了很大的优化,没有具体区分同步还是异步了,全部以异步返回。看这段代码
CompletableFuture<AppResponse> appResponseFuture =
currentClient.request(inv, timeout, executor).thenApply(obj -> (AppResponse) obj);
这里使用了 CompletableFuture 并在结束后返回对应的结果 AppResponse。currentClient.request
里面的逻辑还是和之前版本一样通过 DefaultFuture.newFuture 创建一个和 reqid 关联的 future,不过是多了异步执行器 executor。通过 AsyncRpcResult
对future 进行了封装并返回。返回后在下面逻辑中进行同步等待,当然如果不需要同步的则直接返回。
再看下面逻辑
同步等待时候,如果有执行器则会先执行执行器的逻辑,最后就是等待结果返回。
那结果在什么时候返回呢?
还是在 org.apache.dubbo.remoting.exchange.support.DefaultFuture#doReceived
处
只不过这里用到了 CompletableFuture 的特性来设置结果并触发同步等待结果。
总结
其实准确的来说并不是2.7.x和3.0的差异,应该是2.7.x中间某个版本开始就改掉,我觉得这个不重要所以没去细查到底是哪个版本开始改的。重要的是我们知道dubbo为了解决netty的异步转同步问题到底做了哪些处理,虽然说是两种方式,其实本质上还是一个理念:多线程下通过共享的变量进行同步。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。