本篇次要介绍一下 dubbo 2.7.x 以及 3.0 版本是如何实现 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 的异步转同步问题到底做了哪些解决,尽管说是两种形式,其实实质上还是一个理念:多线程下通过共享的变量进行同步。