前言
前面看完了dubbo的服务发布和引用相关的内容
dubbo的服务发布、dubbo的服务引用、dubbo的服务调用-集群容错和路由负载
本篇看一下dubbo的一个rpc调用的过程
先看下官网的调用链:
一. 服务发起调用
通过之前的服务引用和发布相关内容,对相关知识已经有部分的了解,当一个已经引用进来的服务,对其发起调用的时候会通过InvokerInvocationHandler
代理发起调用:
通过这个invoker.invoke(rpcInvocation)
会来到org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker#invoke
这个方法中有if else判断,这里其实就是对服务调用时的服务降级进行处理,不了解的可以看:dubbo的服务调用-集群容错和路由负载
然后进入:invoker.invoke(invocation)
会走到org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker#invoke
这里的list(invocation)
是从directory服务目录和router路由等获取可用的Invoker,然后initLoadBalance(invokers, invocation)
是动态获取一个负载均衡策略,继续到doInvoke(invocation, invokers, loadbalance)
就是带着可用的Invoker和对应的负载均衡策略去执行,不了解的可以看:dubbo的服务调用-集群容错和路由负载,接着进入doInvoke(invocation, invokers, loadbalance)
其实是进入集群容错的相关代码,集群容错有多种策略,这里我们已常用的失败重试为例进入查看:org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke
这个方法中先获取服务配置的重试次数,然后根据重试次数进行for循环,在每次循环中都根据前面获取的可用Invokers和获取的负载均衡loadbalance选择一个可用的Invoker去执行,如果执行成功直接返回,不成功就记录错误并下次循环
这个方法中的select(loadbalance, invocation, copyInvokers, invoked)
就是从负载均衡算法中获取一个被选中的Invoker,不了解的还是看:dubbo的服务调用-集群容错和路由负载
继续跟进入invoker.invoke(invocation)
,进入之后就到了dubbo的扩展点,也就是各种拦截器,很长的一个调用链:
org.apache.dubbo.rpc.protocol.InvokerWrapper#invoke
org.apache.dubbo.rpc.Invoker#invoke
org.apache.dubbo.rpc.filter.ConsumerContextFilter#invoke
org.apache.dubbo.monitor.support.MonitorFilter#invoke
org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter#invoke
org.apache.dubbo.rpc.listener.ListenerInvokerWrapper#invoke
org.apache.dubbo.rpc.protocol.AbstractInvoker#invoke
这一串调用链就是dubbo的扩展点,关于这里可以看官网的文章:扩展点加载,这些扩展点具体都是通过SPI加载的实例,通过扩展点自动包装、扩展点自动装配、扩展点自适应、扩展点自动激活这些方式进行调用链组装,然后我们来到org.apache.dubbo.rpc.protocol.AbstractInvoker#invoke
进入doInvoke(invocation)
会来到org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
进入currentClient.request
到达org.apache.dubbo.rpc.protocol.dubbo.ReferenceCountExchangeClient#request(java.lang.Object, int, java.util.concurrent.ExecutorService)
➡️ org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient#request(java.lang.Object, int, java.util.concurrent.ExecutorService)
➡️ org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient#request(java.lang.Object, int, java.util.concurrent.ExecutorService)
➡️ org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#request(java.lang.Object, int, java.util.concurrent.ExecutorService)
这里就是创建了一个Request
并channel.send(req)
,继续跟进下一步走到:org.apache.dubbo.remoting.transport.AbstractPeer#send
然后进入netty:org.apache.dubbo.remoting.transport.netty4.NettyChannel#send
通过channel.writeAndFlush(message)
把数据写出去,到这里调用端发起请求就完成了
二. 服务提供端收到调用请求
在调用端通过netty发起请求之后,服务提供端会在org.apache.dubbo.remoting.transport.netty.NettyHandler#messageReceived
中收到netty通道中发来的请求消息
进入 handler.received(channel, e.getMessage())
来到:org.apache.dubbo.remoting.transport.AbstractPeer#received
➡️ org.apache.dubbo.remoting.transport.MultiMessageHandler#received
➡️ org.apache.dubbo.remoting.exchange.support.header.HeartbeatHandler#received
➡️
org.apache.dubbo.remoting.transport.dispatcher.all.AllChannelHandler#received
这个方法进入之后获取一个线程池,把接收的请求交给线程池去处理。所以代码会走到org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable#run
这里我们是接收调用,所以跟进handler.received(channel, message);
调用到org.apache.dubbo.remoting.transport.DecodeHandler#received
解码处理
这个方法对接收的消息进行解码,然后继续调用到org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#received
由于我们这里是提供方接收请求,所以自然是进入message instanceof Request
,然后会走到org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleRequest
这个方法中先构建了一个Response
,然后拿到请求里的数据,然后handler.reply(channel, msg)
,这一步是异步去执行调用代码,构建了一个CompletableFuture
,这个CompletableFuture
是jdk8新增的一个异步处理工具,大家可以百度下。然后下面future.whenComplete
等待(CompletableFuture.whenComplete表示同一个线程去执行)执行的结果,拿到执行结果后顺着netty通道又把处理结果写回给调用方。这里我们先看下handler.reply(channel, msg)
,实际调用到了org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter#reply
这个方法从请求数据中获取到Invoker:getInvoker(channel, inv)
这里这个exporterMap是不是很熟悉,就是我们之前将服务发布和的时候,最终把发布的服务存到一个map中,就是这个map,这里就是从map中获取一个Exporter并从中提取出来Invoker
然后Result result = invoker.invoke(inv);
去执行,然后从这里开始又进入dubbo的扩展点链路:
org.apache.dubbo.rpc.Invoker#invoke
org.apache.dubbo.rpc.filter.EchoFilter#invoke
org.apache.dubbo.rpc.filter.ClassLoaderFilter#invoke
org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter#invoke
org.apache.dubbo.monitor.support.MonitorFilter#invoke
org.apache.dubbo.rpc.filter.TimeoutFilter#invoke
org.apache.dubbo.rpc.filter.ExceptionFilter#invoke
org.apache.dubbo.rpc.protocol.InvokerWrapper#invoke
调用链执行完成后会到达:org.apache.dubbo.rpc.proxy.AbstractProxyInvoker#invoke
这个方法第一步就是doInvoke
,跟进会到达:org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getInvoker
这里就是代理的实际的调用接口,也就是我们自己写的目标接口实现,然后会执行我们自己写的实现逻辑获取结果。
拿到结果后回到org.apache.dubbo.rpc.proxy.AbstractProxyInvoker#invoke
,把结果构建成CompletableFuture
通知处理完成,就回到了org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleRequest
这里future.whenComplete
会等待获取到结果,然后立即触发到这里,把结果拿到执行channel.send(res)
,这里就和调用端发送请求数据类似了,会走到:org.apache.dubbo.remoting.transport.AbstractPeer#send
➡️ org.apache.dubbo.remoting.transport.netty4.NettyChannel#send
顺着通道写回数据,到这里服务提供者就完事了,在接收到请求方的请求后作出响应并把结果通过netty告诉给请求方。
三. 请求方接收服务提供方的执行结果
当服务提供方把结果通过netty发回调用方的时候,服务调用方也是在org.apache.dubbo.remoting.transport.netty.NettyHandler#messageReceived
里面收到数据,这里和服务提供方收到调用方的请求是类似的
然后handler.received(channel, e.getMessage())
跟进:org.apache.dubbo.remoting.transport.AbstractPeer#send
➡️ org.apache.dubbo.remoting.transport.MultiMessageHandler#received
➡️ org.apache.dubbo.remoting.exchange.support.header.HeartbeatHandler#received
➡️ org.apache.dubbo.remoting.transport.dispatcher.all.AllChannelHandler#received
➡️ org.apache.dubbo.remoting.transport.dispatcher.ChannelEventRunnable#run
➡️ org.apache.dubbo.remoting.transport.DecodeHandler#received
➡️ org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#received
到这里就和之前的不一样了,之前是请求Request
,现在我们接收服务提供者的返回数据,所以是message instanceof Response
:org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleResponse
跟进代码会来到:org.apache.dubbo.remoting.exchange.support.DefaultFuture#received(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.exchange.Response, boolean)
这个方法进来先从org.apache.dubbo.remoting.exchange.support.DefaultFuture#FUTURES
中根据请求id删除一个,如果future存在,那么,跟进方法会来到:org.apache.dubbo.remoting.exchange.support.DefaultFuture#doReceived
这里调用了java.util.concurrent.CompletableFuture#complete
通知拿到了结果,这里就是CompletableFuture
的用法了。
其实可以理解为在发起请求的时候,创建了一个CompletableFuture,然后等请求结果拿到之后,在通过CompletableFuture的回调,拿到执行的结果。
然后把结果返回到我们实际调用的地方,这样我们就拿到rpc调用的返回结果了… … … . . . …
md 这里是新版本对调用方接收rpc请求返回结果的异步处理,我还没搞懂,等我搞懂了补上TvT