概述
上文消费端服务调用中描述了发起一次远程调用的调用链,解析到了触发了Netty的outBound写事件writeAndFlush,将请求编码发送,但一次远程调用其实并没有真正完成,完整的一次远程调用还应包括接受服务端返回数据,将其返回给业务方。本文会继续上文做余下工作的解析。
在服务端,据前面对Netty的分析可知,NioEventLoop会监听OP_READ事件,收到OP_READ事件之后,会对其进行处理,然后触发ChannelRead入站事件,在pipiline中传播,会先经过解码handler做解码操作,然后会传播到NettyServerHandler做真正的逻辑处理。
消费端的接收响应结果实践逻辑与服务端大致相同,而且没有服务端的请求响应过程甚至更为简单一些,下文会分开介绍。
服务端请求响应源码分析
服务端的请求响应源码分析从NettyServerHandler#channelRead方法开始,代码实现如下:
在这里插入图片描述此处实现较为简单,主要是获取NettyChannel对象,此处的handler为NettyServer,跟进会进入AbstractPeer#received,然后进入MultiMessageHandler#received,因为这两个方法实现非常简单,就不再代码展开,直接跟进到HeartbeatHandle#received,代码实现如下:
在这里插入图片描述此处主要是对心跳请求的处理,心跳请求则新建response设置requestid和心跳返回事件,将其返回,如果是心跳响应直接return。如果是正常远程调用则进入AllChannelRead#received方法,实现代码如下:
在这里插入图片描述前面的操作都是I/O线程处理,此处将派发到业务线程池去执行,因为I/O线程数量有限,因此比较耗时的业务操作一般交由业务线程池处理,以免阻塞I/O线程。
Dubbo中提供了5种线程派发策略(all,direct,message,execution,connection),来决定哪些操作线程由业务线程池处理,哪些操作由I/O线程处理。具体规则如下:
all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行。
message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
继续往下跟进,就要进入业务线程,代码跟踪到ChannelEventRunnable#run方法,代码实现如下:
在这里插入图片描述在上面新建ChannelEventRunnable对象时写入了RECIVED状态,所以此处进入DecodeHandler#received,实现代码如下:
在这里插入图片描述消息解码操作,因为此前已经在解码handler中做过解码操作,所以此处不会执行具体解码逻辑,直接跳过,继续跟进会进入HeaderExchangeHandler#received方法,代码实现如下:
在这里插入图片描述主要对消息类型以及双向还是单向进行分类处理,因为此处是响应消费端的请求,所以进入handleRequest方法,实现代码如下:
在这里插入图片描述其实到了这个地方就能很清楚的看出服务端响应请求的整体逻辑,构造response,处理消费端发来的请求,将结果回写到response中,然后调用channel.send()发送给消费端,发送消息在消费端已经分析过一次,所以这里看到了这个channel.send基本可以盲猜消费端也有一个handler重写了channelRead方法,等待处理服务端返回的结果。
对handler.reply方法作展开,因为此处是Dubbo协议,所以跟进到DubboProtocol#reply方法,代码实现如下:
在这里插入图片描述前面在分析ServiceBean服务暴露过程中描述过,最终会将服务Invoker转换根据对应协议转换成exporter放到exporterMap中,此处就是根据消费发来的请求消息取出,执行其invoke方法。
执行invoke方法之后也会触发拦截器链,这里不再展开描述,直接跟进到最后的AbstractProxyInvoker#invoke方法,实现代码如下:
在这里插入图片描述到了这里终于到了执行我们自己写的service的时候,doInvoke方法默认调用的JavassistProxyFactory返回的AbstractProxyInvoker中重写的doInvoke方法,这里贴一下代码:
在这里插入图片描述这里就看到了invokeMethod方法,获得结果后,调用wrapWithFuture方法获取future对象,这里主要对异步调用做处理,如果是异步则获取异步Future对象,如果不是这直接以执行结果作为result创建一个完成的future,然后是回调构建异步结果对象asyncRpcResult,然后执行返回以及回调,最终数据发送给消费端,同样是触发writeAndFlush出站写事件。
消费端异步回写响应结果
上文中我们盲猜过,消费端肯定有一个handler重写了channelRead方法用于接收服务端的响应结果,所以,进入NettyClientHandler中,就看到了盲猜的结果,代码如下:
在这里插入图片描述看上去和服务端的长得一样,其实,不光长得一样,后续流程也都一样,一直到HeaderExchangeHandler#received方法,进入handleResponse方法,才开始实现其写回结果的逻辑,再贴一遍HeaderExchangeHandler#received方法,代码如下:
在这里插入图片描述此处进入了handleResponse方法,代码实现如下:
在这里插入图片描述可以看到直接调用了DefaultFuture#received方法,具体实现如下:
在这里插入图片描述此处会根据消费端传过来的requestId从保存future对象的map中获取对应的future,设置结果并标记完成,此处再贴一下doReceived方法的实现,代码如下:
在这里插入图片描述根据返回结果状态,标记future对象的完成。上篇文章中调用get方法阻塞等带返回结果的就可以继续往下执行了。
到这里才算完成了一次完整的远程调用。
网友评论