前言
之前已经写了三篇关于DUBBO的了,本文将是DUBBO系列的最后最后一篇,这篇写完之后也就对DUBBO的整个初始化和调用流程都有了较为详细的了解了,让我们赶紧来做个Ending吧。
源码解析
消费者调用流程涉及到消费者端和生产者端的交互,所以将分为三个部分来讲解,分别是
-消费者发起调用请求
-生产者响应调用请求
-消费者获取调用结果
消费者发起调用请求
之前文章中讲过消费者初始化时最后返回的是一个InvokerInvocationHandler
的代理对象,根据动态代理的原理,DUBBO接口的方法调用都会由invoke
方法代理,我们来看一下其实现
1 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
正常情况下的方法调用会走invoker.invoke(new RpcInvocation(method, args)).recreate()
这个分支,首先来看new RpcInvocation(method, args)
1 | public RpcInvocation(Method method, Object[] arguments) { |
非常简单的一个初始化赋值操作,就不做过多讲解了,接着回头看invoker.invoke(new RpcInvocation(method, args))
方法,这里的invoker
之前也说过了,是一个通过SPI机制生成的对象,以默认设置的参数failover
为例,这里的invoker
就是一个MockClusterInvoker
对象中包含了一个FailoverClusterInvoker
对象引用的类似链式的对象,那么我们来详细看看MockClusterInvoker
的invoke
方法
1 | public Result invoke(Invocation invocation) throws RpcException { |
当没有配置mock
值时,value
值得到的是默认值false
,会去执行result = this.invoker.invoke(invocation)
,this.invoker
刚才提到过了是一个FailoverClusterInvoker
类型的对象,但该对象并没有实现invoke
方法,实际上该方法是继承自父类AbstractClusterInvoker
的,来看一下
1 | public Result invoke(final Invocation invocation) throws RpcException { |
这里的list(invocation)
方法根据invocation
中的参数来获取所有的invoker
列表,就不深入讲了,接着来看loadbalance
对象的生成,loadbalance
对象根据SPI机制生成,具体实现由loadbalance
参数决定,也就是具体的负载均衡策略,DUBBO提供的实现有random
、roundrobin
、leastactive
、consistenthash
四种,其中没有根据服务端负载进行调节的策略。其中默认实现为random
,生成的loadbalance
就是一个RandomLoadBalance
的对象。本次只分析同步的接口调用方式,跳过RpcUtils.attachInvocationIdIfAsync
,接着看doInvoke(invocation, invokers, loadbalance)方法,该方法实现在FailoverClusterInvoker
中
1 | public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { |
这里select(loadbalance, invocation, copyinvokers, invoked)
方法根据传入的loadbalance
对象挑选出一个执行用的invoker
,里面调用链较深,在此不做详细分析。最终将通过invoker.invoke(invocation)
进行调用并返回一个Result
类型的对象,也就是最终的执行结果,这里的invoker
对象是InvokerWrapper
的实例,该实例引用了一个ListenerInvokerWrapper
的实例,接着又链式引用了AbstractInvoker
的实例,因此最终执行的invoke
方法在AbstractInvoker
中,来看一下
1 | public Result invoke(Invocation inv) throws RpcException { |
这里的关键方法是doInvoke(invocation)
,其实现在具体的Invoker
实现类中,这里我们采用的是默认的dubbo协议,所以实现类为DubboInvoker
,来看看其doInvoke
方法
1 | @Override |
这里的isOneway
和isAsync
两个标志位分别区分单向调用(不在乎调用结果)和异步调用,这里我们分析同步调用的流程,这里的currentClient
是一个ReferenceCountExchangeClient
类型的对象
1 | public ResponseFuture request(Object request) throws RemotingException { |
这里的client
是一个HeaderExchangeClient
类型的对象,
1 | public ResponseFuture request(Object request) throws RemotingException { |
这里的channel
是一个HeaderExchangeChannel
类型的对象,继续跟进去
1 | public ResponseFuture request(Object request) throws RemotingException { |
这里的request
方法自己又进行了一次内部调用,可以看到具体实现时创建了一个DefaultFuture
对象并且通过channel.send(req)
方法发送请求到生产者端,这里不做具体深入了。接着我们跳回DubboInvoker
类doInvoke
方法中的currentClient.request(inv, timeout).get()
,这里是不是和jdk中future的用法很像,事实上这里也确实是通过get
方法的调用将线程阻塞在这里等待结果,从而将异步调用转化为同步。为了证实这个想法,我们来看看DefaultFuture
的get
方法
1 | public Object get() throws RemotingException { |
从done.await(timeout, TimeUnit.MILLISECONDS)
可以看到这里不仅是等待isDone()
这个状态位,同时还有超时时间的限制。isDone()
判断的是什么,来看一下
1 | public boolean isDone() { |
判断response
对象是否为空,那么后面的流程其实不难猜,生产者处理完结果会来填充response
。
生产者响应调用请求
生产者开启了端口监听,消息的解码由Netty处理,解码后交由NettyHandler
的messageReceived
方法进行业务处理,来看一下
1 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { |
先来看一下NettyChannel.getOrAddChannel
1 | static NettyChannel getOrAddChannel(org.jboss.netty.channel.Channel ch, URL url, ChannelHandler handler) { |
主要是从channelMap
中获取对应的NettyChannel
,接着回到NettyHandler
的messageReceived
方法来看handler.received(channel, e.getMessage())
,这里的handler
是一个NettyServer
的实例,但它本身没有实现received
方法,该方法要追溯到它的父类的父类的父类(真的就是这么长的继承关系。。。)AbstractPeer
中,来看一下
1 | public void received(Channel ch, Object msg) throws RemotingException { |
这里的handler
是MultiMessageHandler
对象的实例,来看一下其received
方法的实现
1 |
|
这里的handler
又是HeartbeatHandler
类的实例
1 | public void received(Channel channel, Object message) throws RemotingException { |
因为不是心跳类的消息,所以执行handler.received(channel, message)
继续这个调用链,这里的handler
是AllChannelHandler
类型的
1 | public void received(Channel channel, Object message) throws RemotingException { |
这里终于结束了调用链,转而启动了一个线程池来执行任务,那我们来看看具体的任务线程ChannelEventRunnable
中到底需要执行什么任务
1 | public void run() { |
这里传入的是RECEIVED
状态,执行对应分支又是调用handler.received(channel, message)
,好吧继续。。。
这里的handler
是DecodeHandler
的实例,继续跟下去
1 | public void received(Channel channel, Object message) throws RemotingException { |
调用链还在继续,这次的handler
是HeaderExchangeHandler
类型
1 | public void received(Channel channel, Object message) throws RemotingException { |
正常同步请求会开始执行handleRequest(exchangeChannel, request)
处理请求,并通过channel.send(response)
回复结果,来重点看一下handleRequest
方法
1 | Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException { |
可以看出正常请求将由handler.reply(channel, msg)
处理,这里的handler
是DubboProtocol
中的一个ExchangeHandlerAdapter
实现,其reply
方法如下
1 | public Object reply(ExchangeChannel channel, Object message) throws RemotingException { |
这里一共做了两件事,先通过getInvoker(channel, inv)
获取具体的invoker
,再通过invoker.invoke(inv)
执行获取结果,先来看一下getInvoker(channel, inv)
1 | Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException{ |
这里又看到了熟悉的exporterMap
,之前讲生产者初始化的时候就说过这个map中放入了封装过的Invoker
对象exporter
,现在又把它取了出了并通过getInvoker()
方法获得封装在其中的Invoker
对象。
接着来看invoker.invoke(inv)
方法,其实现首先在InvokerWrapper
类中
1 | public Result invoke(Invocation invocation) throws RpcException { |
然后会调用到AbstractProxyInvoker
中的invoke
方法
1 | public Result invoke(Invocation invocation) throws RpcException { |
这里doInvoke
方法的实现在JavassistProxyFactory
中getInvoker
方法中
1 | public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { |
这里根据传入的 proxy
对象的类信息创建对它的包装对象Wrapper
并调用其invokeMethod
方法,通过传入的参数来调用proxy
对象的对应方法,返回调用结果,也就是执行具体的业务。
完成handleRequest(exchangeChannel, request)
方法的解析后,回到HeaderExchangeHandler
类中接着来看一下channel.send(response)
,这里的channel
传入的是NettyChannel
类型的对象,send
方法的实现在其父类的父类AbstractPeer
中,来看一下
1 | public void send(Object message) throws RemotingException { |
其具体实现又在NettyChannel
中
1 | public void send(Object message, boolean sent) throws RemotingException { |
可以看到业务处理结果最后通过ChannelFuture
对象进行了发送,到此生产者端的任务就完成了。
消费者获取调用结果
这里消费者端通过NETTY从生产者端获取数据的流程和之前的如出一辙,调用链直到HeaderExchangeHandler
之前都是一样的,我们先来回顾一下HeaderExchangeHandler
的received
方法
1 | public void received(Channel channel, Object message) throws RemotingException { |
之前走的是Request分支,这次因为是响应消息走的是Response分支,那么来看一下handleResponse(channel, (Response) message)
的具体实现
1 | static void handleResponse(Channel channel, Response response) throws RemotingException { |
继续跟进去看received
方法
1 | public static void received(Channel channel, Response response) { |
继续看doReceived
干了什么
1 | private void doReceived(Response res) { |
看到这里把执行结果赋值给response
,正好应证了我们之前的猜想,消费者的同步阻塞也就可以继续执行下去了,这也算是非常经典的异步转同步的实现方案了吧。
总结
本文把消费者端和生产者端交互的大概流程进行了讲解,流程主要分为三个部分,分别是:消费者发起调用请求、生产者响应调用请求和消费者获取调用结果,概括一下就是消费者通过生成的代理对象调用invoke
方法通过Netty的通道去请求生产者的exporter
进行执行,并且通过future
的方式将异步的交互转为了同步响应。