第四章 dubbo源码解析目录

9.1 客户端发起请求源码

来看一下客户端请求代码:

1         DemoService demoService = (DemoService) context.getBean("demoService"); // 获取远程服务代理
2         String hello = demoService.sayHello("world"); // 执行远程方法

8.2 构建客户端源码解析中我们看到最终得到的demoService是一个proxy0代理对象。现在来分析第二行代码。

一 客户端请求总体流程

复制代码
//代理发出请求
proxy0.sayHello(String paramString) -->InvokerInvocationHandler.invoke(Object proxy, Method method, Object[] args) -->new RpcInvocation(method, args) -->MockClusterInvoker.invoke(Invocation invocation)//服务降级的地方 //ClusterInvoker将多个Invoker伪装成一个集群版的Invoker -->AbstractClusterInvoker.invoke(final Invocation invocation) //获取Invokers -->list(Invocation invocation) -->AbstractDirectory.list(Invocation invocation) -->RegistryDirectory.doList(Invocation invocation)//从Map<String, List<Invoker<T>>> methodInvokerMap中获取key为sayHello的List<Invoker<T>> -->MockInvokersSelector.getNormalInvokers(final List<Invoker<T>> invokers)//对上述的List<Invoker<T>>再进行一次过滤(这里比如说过滤出所有协议为mock的Invoker,如果一个也没有就全部返回),这就是router的作用 //获取负载均衡器 -->loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl() .getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE))//默认为random -->RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation)//异步操作添加invocationID -->FailoverClusterInvoker.doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) //使用负载均衡器选择一个Invoker出来:RegistryDirectory$InvokerDelegete实例 -->AbstractClusterInvoker.select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) -->doselect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) -->AbstractLoadBalance.select(List<Invoker<T>> invokers, URL url, Invocation invocation) -->RandomLoadBalance.doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) //执行listener和filter链 -->ListenerInvokerWrapper.invoke -->ConsumerContextFilter.invoke(Invoker<?> invoker, Invocation invocation)//设置一些RpcContext属性,并且设置invocation中的invoker属性 -->FutureFilter.invoke(Invocation invocation) -->MonitorFilter.invoke(Invocation invocation)//monitor在这里收集数据 -->AbstractInvoker.invoke(Invocation inv)//重新设置了invocation中的invoker属性和attachment属性 -->DubboInvoker.doInvoke(final Invocation invocation) //获取ExchangeClient进行消息的发送 -->ReferenceCountExchangeClient.request(Object request, int timeout) -->HeaderExchangeClient.request(Object request, int timeout) -->HeaderExchangeChannel.request(Object request, int timeout) -->AbstractClient.send(Object message, boolean sent)//NettyClient的父类 -->getChannel()//NettyChannel实例,其内部channel实例=NioClientSocketChannel实例 -->NettyChannel.send(Object message, boolean sent) -->NioClientSocketChannel.write(Object message)//已经是netty的东西了,这里的message=Request实例:最重要的是RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={path=com.alibaba.dubbo.demo.DemoService, interface=com.alibaba.dubbo.demo.DemoService, version=0.0.0}]
复制代码

总体流程:

  • 将请求参数(方法名,方法参数类型,方法参数值,服务名,附加参数)封装成一个Invocation
    • 附加参数中的path:即接口名,将会用于服务端接收请求信息后从exportMap中选取Exporter实例
    • 方法名,方法参数类型,方法参数值:将用于JavassistProxyFactory$AbstractProxyInvoker执行对应的方法
  • 使用Directory从Map<String, List<Invoker<T>>> methodInvokerMap中获取key为sayHello(指定方法名)的List<Invoker<T>>
  • 使用Router对上述的List<Invoker<T>>再进行一次过滤,得到subList
  • 使用LoadBalancer从subList中再获取一个Invoker,实际上是InvokerDelegete实例
  • 使用InvokerDelegete实例执行真正的DubboInvoker的listener和filter链,然后执行到真正的DubboInvoker
  • DubboInvoker使用NettyClient向服务端发出了请求

二 源码分析

首先来看proxy0.sayHello

复制代码
 1     public String sayHello(String paramString) {
 2         Object[] arrayOfObject = new Object[1];
 3         arrayOfObject[0] = paramString;
 4         Object localObject = null;
 5         try {
 6             localObject = this.handler.invoke(this, DemoService.class.getMethod("sayHello"), arrayOfObject);
 7         } catch (Throwable e) {
 8             // TODO Auto-generated catch block
 9             e.printStackTrace();
10         }
11         return (String) localObject;
12     }
复制代码

这里的handler就是InvokerInvocationHandler

复制代码
 1 public class InvokerInvocationHandler implements InvocationHandler {
 2     private final Invoker<?> invoker;//MockClusterInvoker实例
 3 
 4     public InvokerInvocationHandler(Invoker<?> handler) {
 5         this.invoker = handler;
 6     }
 7 
 8     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 9         String methodName = method.getName();
10         Class<?>[] parameterTypes = method.getParameterTypes();
11         if (method.getDeclaringClass() == Object.class) {
12             return method.invoke(invoker, args);
13         }
14         if ("toString".equals(methodName) && parameterTypes.length == 0) {
15             return invoker.toString();
16         }
17         if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
18             return invoker.hashCode();
19         }
20         if ("equals".equals(methodName) && parameterTypes.length == 1) {
21             return invoker.equals(args[0]);
22         }
23         return invoker.invoke(new RpcInvocation(method, args)).recreate();
24     }
25 }
复制代码

首先将请求参数封装成一个RpcInvocation实例,如下:

-->String methodName=sayHello
-->Class<?>[] parameterTypes=[class java.lang.String]
-->Object[] arguments=[world]
-->Map<String, String> attachments={}

之后使用MockClusterInvoker.invoke(Invocation invocation)进行远程调用:

复制代码
 1     private final Directory<T> directory;//RegistryDirectory
 2     private final Invoker<T> invoker;//FailoverClusterInvoker
 3 
 4     /**
 5      * 这里实际上会根据配置的mock参数来做服务降级:
 6      * 1 如果没有配置mock参数或者mock=false,则进行远程调用;
 7      * 2 如果配置了mock=force:return null,则直接返回null,不进行远程调用;
 8      * 3 如果配置了mock=fail:return null,先进行远程调用,失败了在进行mock调用。
 9      */
10     public Result invoke(Invocation invocation) throws RpcException {
11         Result result = null;
12         //sayHello.mock->mock->default.mock
13         String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
14         if (value.length() == 0 || value.equalsIgnoreCase("false")) {
15             //no mock
16             result = this.invoker.invoke(invocation);
17         } else if (value.startsWith("force")) {
18             if (logger.isWarnEnabled()) {
19                 logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
20             }
21             //force:direct mock
22             result = doMockInvoke(invocation, null);
23         } else {
24             //fail-mock
25             try {
26                 result = this.invoker.invoke(invocation);
27             } catch (RpcException e) {
28                 if (e.isBiz()) {
29                     throw e;
30                 } else {
31                     if (logger.isWarnEnabled()) {
32                         logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
33                     }
34                     result = doMockInvoke(invocation, e);
35                 }
36             }
37         }
38         return result;
39     }
复制代码

注意:这里可以做服务降级,后续会说。

之后调用FailoverClusterInvoker.invoke方法,该方法在其父类AbstractClusterInvoker中,

复制代码
 1     protected final Directory<T> directory;//RegistryDirectory    
 2     
 3     public Result invoke(final Invocation invocation) throws RpcException {
 4         ...
 5         LoadBalance loadbalance;
 6 
 7         List<Invoker<T>> invokers = list(invocation);
 8         if (invokers != null && invokers.size() > 0) {
 9             loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
10                     .getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
11         } else {
12             loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
13         }
14         RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);//异步调用加调用ID
15         return doInvoke(invocation, invokers, loadbalance);
16     }
17 
18     protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
19         List<Invoker<T>> invokers = directory.list(invocation);
20         return invokers;
21     }
复制代码

首先是获取一个List<Invoker<T>>,之后获取一个LoadBalance,最后调用doInvoke进行调用。

首先来看通过RegistryDirectory.list(Invocation invocation),该方法在RegistryDirectory的父类AbstractDirectory中:

复制代码
 1     private volatile List<Router> routers;
 2     public List<Invoker<T>> list(Invocation invocation) throws RpcException {
 3         ...
 4         List<Invoker<T>> invokers = doList(invocation);
 5         List<Router> localRouters = this.routers; // local reference
 6         if (localRouters != null && localRouters.size() > 0) {
 7             for (Router router : localRouters) {
 8                 try {
 9                     if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, true)) {
10                         invokers = router.route(invokers, getConsumerUrl(), invocation);
11                     }
12                 } catch (Throwable t) {
13                     logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
14                 }
15             }
16         }
17         return invokers;
18     }
复制代码

首先执行doList(invocation)方法获取出List<Invoker<T>>,之后使用router循环过滤,最后返回过滤后的List<Invoker<T>>。

RegistryDirectory.doList(invocation)

复制代码
 1     public List<Invoker<T>> doList(Invocation invocation) {
 2         ...
 3         List<Invoker<T>> invokers = null;
 4         Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
 5         if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
 6             String methodName = RpcUtils.getMethodName(invocation);
 7             Object[] args = RpcUtils.getArguments(invocation);
 8             if (args != null && args.length > 0 && args[0] != null
 9                     && (args[0] instanceof String || args[0].getClass().isEnum())) {
10                 invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由 sayHello.world
11             }
12             if (invokers == null) {
13                 invokers = localMethodInvokerMap.get(methodName);
14             }
15             if (invokers == null) {
16                 invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
17             }
18             if (invokers == null) {
19                 Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
20                 if (iterator.hasNext()) {
21                     invokers = iterator.next();
22                 }
23             }
24         }
25         return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
26     }
复制代码

其中Map<String, List<Invoker<T>>> methodInvokerMap在8.2 构建客户端源码解析已经初始化好了:

Map<String, List<Invoker<T>>> methodInvokerMap={
sayHello=[provider1的RegistryDirectory$InvokerDelegete实例, provider2的RegistryDirectory$InvokerDelegete实例], *=[provider1的RegistryDirectory$InvokerDelegete实例, provider2的RegistryDirectory$InvokerDelegete实例]}

这里根据方法名sayHello取出两个RegistryDirectory$InvokerDelegete实例。最后通过Router进行过滤,这里只有一个Router,就是MockInvokersSelector。

复制代码
 1     public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
 2                                       URL url, final Invocation invocation) throws RpcException {
 3         if (invocation.getAttachments() == null) {
 4             return getNormalInvokers(invokers);
 5         } else {
 6             String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
 7             if (value == null)
 8                 return getNormalInvokers(invokers);
 9             else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
10                 return getMockedInvokers(invokers);
11             }
12         }
13         return invokers;
14     }
15 
16     private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers) {
17         if (!hasMockProviders(invokers)) {
18             return invokers;
19         } else {
20             List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());
21             for (Invoker<T> invoker : invokers) {
22                 if (!invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
23                     sInvokers.add(invoker);
24                 }
25             }
26             return sInvokers;
27         }
28     }
复制代码

这里直接返回了。到此就已经选出可以被调用的RegistryDirectory$InvokerDelegete实例子集了。记下来先获取负载均衡器,默认是RandomLoadBalance。最后执行FailoverClusterInvoker.

doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance):

复制代码
 1     public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
 2         List<Invoker<T>> copyinvokers = invokers;
 3         checkInvokers(copyinvokers, invocation);
 4         int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;//默认是2+1次
 5         if (len <= 0) {
 6             len = 1;
 7         }
 8         // retry loop.
 9         RpcException le = null; // last exception.
10         List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
11         Set<String> providers = new HashSet<String>(len);
12         for (int i = 0; i < len; i++) {
13             //重试时,进行重新选择,避免重试时invoker列表已发生变化.
14             //注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
15             if (i > 0) {
16                 checkWhetherDestroyed();
17                 copyinvokers = list(invocation);
18                 //重新检查一下
19                 checkInvokers(copyinvokers, invocation);
20             }
21             Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
22             invoked.add(invoker);
23             RpcContext.getContext().setInvokers((List) invoked);
24             try {
25                 Result result = invoker.invoke(invocation);
26                 ...
27                 return result;
28             } catch (RpcException e) {
29                 if (e.isBiz()) { // biz exception.
30                     throw e;
31                 }
32                 le = e;
33             } catch (Throwable e) {
34                 le = new RpcException(e.getMessage(), e);
35             } finally {
36                 providers.add(invoker.getUrl().getAddress());
37             }
38         }
39         throw new RpcException(le ...);
40     }
复制代码

首先使用负载均衡器获取一个RegistryDirectory$InvokerDelegete实例,然后使用选出的RegistryDirectory$InvokerDelegete.invoke进行请求发送。

复制代码
 1     protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
 2         ...
 3         Invoker<T> invoker = doselect(loadbalance, invocation, invokers, selected);
 4         ..
 5         return invoker;
 6     }
 7 
 8     private Invoker<T> doselect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
 9         if (invokers == null || invokers.size() == 0)
10             return null;
11         if (invokers.size() == 1)
12             return invokers.get(0);
13         // 如果只有两个invoker,并且其中一个已经有至少一个被选过了,退化成轮循
14         if (invokers.size() == 2 && selected != null && selected.size() > 0) {
15             return selected.get(0) == invokers.get(0) ? invokers.get(1) : invokers.get(0);
16         }
17         Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
18 
19         //如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试.
20         if ((selected != null && selected.contains(invoker))
21                 || (!invoker.isAvailable() && getUrl() != null && availablecheck)) {
22             try {
23                 Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
24                 ...
25             } catch (Throwable t) {
26                ...
27             }
28         }
29         return invoker;
30     }
复制代码

RandomLoadBalance.doSelect

复制代码
1     private final Random random = new Random();
2 
3     protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
4         int length = invokers.size(); // 总个数
5         ...//权重计算
6         // 如果权重相同或权重为0则均等随机
7         return invokers.get(random.nextInt(length));
8     }
复制代码

最后来看RegistryDirectory$InvokerDelegete.invoke,该方法实际在其父类InvokerWrapper中:

1     private final Invoker<T> invoker;//ListenerInvokerWrapper
2 
3     public Result invoke(Invocation invocation) throws RpcException {
4         return invoker.invoke(invocation);
5     }

ListenerInvokerWrapper.invoke

1     private final Invoker<T> invoker;//ProtocolFilterWrapper$Invoker
2 
3     public Result invoke(Invocation invocation) throws RpcException {
4         return invoker.invoke(invocation);
5     }

之后就会执行一系列的filter,这些filter后续会讲,现在直接执行到DubboInvoker.invoke,实际上该方法在其父类AbstractInvoker中,AbstractInvoker又调用了DubboInvoker.doInvoke:

复制代码
 1     private final ExchangeClient[] clients;
 2 
 3     protected Result doInvoke(final Invocation invocation) throws Throwable {
 4         RpcInvocation inv = (RpcInvocation) invocation;
 5         final String methodName = RpcUtils.getMethodName(invocation);
 6         inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
 7         inv.setAttachment(Constants.VERSION_KEY, version);
 8 
 9         ExchangeClient currentClient;
10         if (clients.length == 1) {
11             currentClient = clients[0];//单一长连接。默认
12         } else {
13             currentClient = clients[index.getAndIncrement() % clients.length];
14         }
15         try {
16             boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);//是否异步
17             boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);//是否没有返回值
18             int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
19             if (isOneway) {
20                 boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
21                 currentClient.send(inv, isSent);
22                 RpcContext.getContext().setFuture(null);
23                 return new RpcResult();
24             } else if (isAsync) {
25                 ResponseFuture future = currentClient.request(inv, timeout);
26                 RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
27                 return new RpcResult();
28             } else {
29                 RpcContext.getContext().setFuture(null);
30                 return (Result) currentClient.request(inv, timeout).get();
31             }
32         } catch (TimeoutException e) {
33             throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
34         } catch (RemotingException e) {
35             throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
36         }
37     }
复制代码

其中ExchangeClient[] clients在8.2 构建客户端源码解析已经被初始化好了:

1 ExchangeClient[] clients = [ReferenceCountExchangeClient实例]//如果设置了多条连接,此处有多个client

ReferenceCountExchangeClient.request

1     private ExchangeClient client;//HeaderExchangeClient
2 
3     public ResponseFuture request(Object request, int timeout) throws RemotingException {
4         return client.request(request, timeout);
5     }

HeaderExchangeClient.request

1     private final ExchangeChannel channel;//HeaderExchangeChannel
2 
3     public ResponseFuture request(Object request, int timeout) throws RemotingException {
4         return channel.request(request, timeout);
5     }

HeaderExchangeChannel.request

复制代码
 1     private final Channel channel;//NettyClient
 2 
 3     public ResponseFuture request(Object request, int timeout) throws RemotingException {
 4         if (closed) {
 5             throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
 6         }
 7         // create request.
 8         Request req = new Request();
 9         req.setVersion("2.0.0");
10         req.setTwoWay(true);
11         req.setData(request);
12         DefaultFuture future = new DefaultFuture(channel, req, timeout);
13         try {
14             channel.send(req);
15         } catch (RemotingException e) {
16             future.cancel();
17             throw e;
18         }
19         return future;
20     }
复制代码

上边的channel是NettyClient实例,这里的send实际上是调用其父类AbstractClient的父类AbstractPeer,AbstractPeer调用AbstractClient.send:

复制代码
 1     public void send(Object message, boolean sent) throws RemotingException {
 2         if (send_reconnect && !isConnected()) {
 3             connect();
 4         }
 5         Channel channel = getChannel();//NettyChannel
 6         //TODO getChannel返回的状态是否包含null需要改进
 7         if (channel == null || !channel.isConnected()) {
 8             throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl());
 9         }
10         channel.send(message, sent);
11     }
复制代码

NettyChannel.send

复制代码
 1     private final org.jboss.netty.channel.Channel channel;//NioClientSocketChannel
 2 
 3     public void send(Object message, boolean sent) throws RemotingException {
 4         super.send(message, sent);
 5 
 6         boolean success = true;
 7         int timeout = 0;
 8         try {
 9             ChannelFuture future = channel.write(message);
10             if (sent) {
11                 timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
12                 success = future.await(timeout);
13             }
14             Throwable cause = future.getCause();
15             if (cause != null) {
16                 throw cause;
17             }
18         } catch (Throwable e) {
19             throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
20         }
21 
22         if (!success) {
23             throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
24                     + "in timeout(" + timeout + "ms) limit");
25         }
26     }
复制代码

这里就执行到了netty内部,通过netty自己的NioClientSocketChannel将消息发送给服务端。(这里发送之前有编码行为,后续会讲)

一 总体流程图

复制代码
服务端接收请求消息
NettyHandler.messageReceived(ChannelHandlerContext ctx, MessageEvent e)
-->MultiMessageHandler.received(Channel channel, Object message)
  -->HeartbeatHandler.received(Channel channel, Object message)
    -->AllChannelHandler.received(Channel channel, Object message)
      -->ExecutorService cexecutor = getExecutorService()
      -->cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message))
        -->ChannelEventRunnable.run()
          -->DecodeHandler.received(Channel channel, Object message)
            -->decode(Object message)
            -->HeaderExchangeHandler.received(Channel channel, Object message)
              -->Response response = handleRequest(exchangeChannel, request)
                -->DubboProtocol.requestHandler.reply(ExchangeChannel channel, Object message)//这里的message就是上边的RpcInvocation
		  //首先获取exporter,之后再获取invoker
		  -->getInvoker(Channel channel, Invocation inv)//组装serviceKey=com.alibaba.dubbo.demo.DemoService:20880
		    -->(DubboExporter<?>) exporterMap.get(serviceKey)//从Map<String, Exporter<?>> exporterMap中根据serviceKey获取DubboExport实例,
		    -->exporter.getInvoker()//获取RegistryProtocol$InvokerDelegete实例
		  //执行filter链
		  -->EchoFilter.invoke(Invoker<?> invoker, Invocation inv)
		    -->ClassLoaderFilter.nvoke(Invoker<?> invoker, Invocation invocation)
		      -->GenericFilter.invoke(Invoker<?> invoker, Invocation inv)
		        -->ContextFilter.invoke(Invoker<?> invoker, Invocation invocation)
			  -->TraceFilter.invoke(Invoker<?> invoker, Invocation invocation)
			    -->TimeoutFilter.invoke(Invoker<?> invoker, Invocation invocation)
			      -->MonitorFilter.invoke(Invoker<?> invoker, Invocation invocation)
			        -->ExceptionFilter.invoke(Invoker<?> invoker, Invocation invocation)
			          //执行真正的invoker调用
				  -->AbstractProxyInvoker.invoke(Invocation invocation)
			            -->JavassistProxyFactory$AbstractProxyInvoker.doInvoke
				      -->Wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments)
					-->DemoServiceImpl.sayHello(String name)
			            -->new RpcResult(Object result)//将返回值result包装成RpcResult(最后该参数会被包装为Response)
	      服务端发送响应消息
              -->channel.send(response)//NettyChannel
                -->NioAcceptedSocketChannel.write(Object message)//已经是netty的东西了,这里的message=Response实例:最重要的是RpcResult [result=Hello world, response form provider: 10.211.55.2:20880, exception=null]
复制代码

 

二 源码解析

netty通信是在netty的handler中进行消息的接收处理和发送。来看一下NettyServer的handler。

复制代码
 1     protected void doOpen() throws Throwable {
 2         ...
 3         final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
 4         ...
 5         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
 6             public ChannelPipeline getPipeline() {
 7                 NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
 8                 ChannelPipeline pipeline = Channels.pipeline();
 9                 pipeline.addLast("decoder", adapter.getDecoder());
10                 pipeline.addLast("encoder", adapter.getEncoder());
11                 pipeline.addLast("handler", nettyHandler);
12                 return pipeline;
13             }
14         });
15         ...
16     }
复制代码

NettyHandler.messageReceived

复制代码
 1     private final ChannelHandler handler;//NettyServer
 2 
 3     @Override
 4     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
 5         NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
 6         try {
 7             handler.received(channel, e.getMessage());
 8         } finally {
 9             NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
10         }
11     }
复制代码

首先会执行NettyServer父类AbstractPeer的received方法,其调用MultiMessageHandler.received:

复制代码
 1     protected ChannelHandler handler;//HeartbeatHandler
 2     public void received(Channel channel, Object message) throws RemotingException {
 3         if (message instanceof MultiMessage) {
 4             MultiMessage list = (MultiMessage) message;
 5             for (Object obj : list) {
 6                 handler.received(channel, obj);
 7             }
 8         } else {
 9             handler.received(channel, message);
10         }
11     }
复制代码

HeartbeatHandler.received(Channel channel, Object message)

复制代码
 1     protected ChannelHandler handler;//AllChannelHandler
 2     public void received(Channel channel, Object message) throws RemotingException {
 3         setReadTimestamp(channel);
 4         if (isHeartbeatRequest(message)) {
 5             ...
 6             return;
 7         }
 8         if (isHeartbeatResponse(message)) {
 9            ...
10             return;
11         }
12         handler.received(channel, message);
13     }
复制代码

AllChannelHandler.received(Channel channel, Object message)

复制代码
 1     protected final ExecutorService executor;//ThreadPoolExecutor
 2     protected final ChannelHandler handler;//DecodeHandler
 3 
 4     public void received(Channel channel, Object message) throws RemotingException {
 5         ExecutorService cexecutor = getExecutorService();
 6         try {
 7             cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
 8         } catch (Throwable t) {
 9             ...
10             throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
11         }
12     }
13 
14     private ExecutorService getExecutorService() {
15         ExecutorService cexecutor = executor;
16         if (cexecutor == null || cexecutor.isShutdown()) {
17             cexecutor = SHARED_EXECUTOR;
18         }
19         return cexecutor;
20     }
复制代码

这里首先创建了一个线程任务ChannelEventRunnable,之后丢入线程池进行执行。

ChannelEventRunnable.run()

复制代码
 1     private final ChannelHandler handler;//DecodeHandler
 2     public void run() {
 3         switch (state) {
 4             case CONNECTED:
 5                 ...
 6                 break;
 7             case DISCONNECTED:
 8                 ...
 9                 break;
10             case SENT:
11                 ...              
12                 break;
13             case RECEIVED:
14                 try {
15                     handler.received(channel, message);
16                 } catch (Exception e) {
17                     logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
18                             + ", message is " + message, e);
19                 }
20                 break;
21             case CAUGHT:
22                 ...
23                 break;
24             default:
25                 logger.warn("unknown state: " + state + ", message is " + message);
26         }
27     }
复制代码

 DecodeHandler.received(Channel channel, Object message)

复制代码
 1     protected ChannelHandler handler;//HeaderExchangeHandler
 2     public void received(Channel channel, Object message) throws RemotingException {
 3         if (message instanceof Decodeable) {
 4             decode(message);
 5         }
 6 
 7         if (message instanceof Request) {
 8             decode(((Request) message).getData());//解码
 9         }
10 
11         if (message instanceof Response) {
12             decode(((Response) message).getResult());
13         }
14 
15         handler.received(channel, message);
16     }
复制代码

HeaderExchangeHandler.received(Channel channel, Object message)

复制代码
 1     private final ExchangeHandler handler;//DubboProtocol$ExchangeHandler
 2 
 3     public void received(Channel channel, Object message) throws RemotingException {
 4         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
 5         ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
 6         try {
 7             if (message instanceof Request) {
 8                 // handle request.
 9                 Request request = (Request) message;
10                 if (request.isEvent()) {
11                     handlerEvent(channel, request);
12                 } else {
13                     if (request.isTwoWay()) {
14                         Response response = handleRequest(exchangeChannel, request);
15                         channel.send(response);
16                     } else {
17                         handler.received(exchangeChannel, request.getData());
18                     }
19                 }
20             } else if (message instanceof Response) {
21                 handleResponse(channel, (Response) message);
22             } else if (message instanceof String) {
23                 if (isClientSide(channel)) {
24                     Exception e = new Exception(...);
25                 } else {
26                     String echo = handler.telnet(channel, (String) message);
27                     if (echo != null && echo.length() > 0) {
28                         channel.send(echo);
29                     }
30                 }
31             } else {
32                 handler.received(exchangeChannel, message);
33             }
34         } finally {
35             HeaderExchangeChannel.removeChannelIfDisconnected(channel);
36         }
37     }
38 
39     Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
40         Response res = new Response(req.getId(), req.getVersion());
41         if (req.isBroken()) {
42             Object data = req.getData();
43 
44             String msg;
45             if (data == null) msg = null;
46             else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);
47             else msg = data.toString();
48             res.setErrorMessage("Fail to decode request due to: " + msg);
49             res.setStatus(Response.BAD_REQUEST);
50 
51             return res;
52         }
53         // find handler by message class.
54         Object msg = req.getData();
55         try {
56             // handle data.
57             Object result = handler.reply(channel, msg);
58             res.setStatus(Response.OK);
59             res.setResult(result);
60         } catch (Throwable e) {
61             res.setStatus(Response.SERVICE_ERROR);
62             res.setErrorMessage(StringUtils.toString(e));
63         }
64         return res;
65     }
复制代码

DubboProtocol$ExchangeHandler.reply(ExchangeChannel channel, Object message)

复制代码
1         public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
2             if (message instanceof Invocation) {
3                 Invocation inv = (Invocation) message;
4                 Invoker<?> invoker = getInvoker(channel, inv);
5                 ...
6                 return invoker.invoke(inv);
7             }
8             throw new RemotingException(...);
9         }
复制代码

首先是获取Invoker,之后使用该invoker执行真正调用。

复制代码
 1     protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
 2 
 3     Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
 4         ...
 5         int port = channel.getLocalAddress().getPort();//20880
 6         String path = inv.getAttachments().get(Constants.PATH_KEY);
 7         ...
 8         String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));
 9 
10         DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
11 
12         if (exporter == null)
13             throw new RemotingException(...);
14 
15         return exporter.getInvoker();
16     }
复制代码

这里serviceKey是:com.alibaba.dubbo.demo.DemoService:20880。实际上是group/serviceName:serviceVersion:port。

复制代码
 1     public static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
 2         StringBuilder buf = new StringBuilder();
 3         if (serviceGroup != null && serviceGroup.length() > 0) {
 4             buf.append(serviceGroup);
 5             buf.append("/");
 6         }
 7         buf.append(serviceName);
 8         if (serviceVersion != null && serviceVersion.length() > 0 && !"0.0.0".equals(serviceVersion)) {
 9             buf.append(":");
10             buf.append(serviceVersion);
11         }
12         buf.append(":");
13         buf.append(port);
14         return buf.toString();
15     }
复制代码

Map<String, Exporter<?>> exporterMap在服务暴露时就已经初始化好了。"com.alibaba.dubbo.demo.DemoService:20880"->DubboExporter实例。该实例包含一个呗filter链包裹的Invoker实例:RegistryProtocol$InvokerDelegete实例。

之后开始执行filter链了,直到最后执行到RegistryProtocol$InvokerDelegete.invoke,该方法实际上是在RegistryProtocol$InvokerDelegete的父类InvokerWrapper执行,InvokerWrapper调用AbstractProxyInvoker.invoke(Invocation invocation)。

复制代码
 1     private final T proxy;//DemoServiceImpl实例
 2 
 3     public Result invoke(Invocation invocation) throws RpcException {
 4         try {
 5             return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
 6         } catch (InvocationTargetException e) {
 7             return new RpcResult(e.getTargetException());
 8         } catch (Throwable e) {
 9             throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
10         }
11     }
复制代码

这里先调用子类JavassistProxyFactory$AbstractProxyInvoker.doInvoke,之后将返回结果封装为RpcResult返回。

1 protected Object doInvoke(T proxy, String methodName,
2                                       Class<?>[] parameterTypes,
3                                       Object[] arguments) throws Throwable {
4                 return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
5             }

这里调用了Wrapper类的invokeMethod方法,Wrapper是一个动态生成的类,笔者给出:

复制代码
 1 import com.alibaba.dubbo.common.bytecode.Wrapper;
 2 import java.util.HashMap;
 3 
 4 public class Wrapper1 extends Wrapper {
 5 
 6     public static String[] pns;//property name array
 7     public static java.util.Map pts = new HashMap();//<property key, property value>
 8     public static String[] mns;//method names
 9     public static String[] dmns;//
10     public static Class[] mts0;
11     /**
12      * @param o  实现类
13      * @param n  方法名称
14      * @param p  参数类型
15      * @param v  参数名称
16      * @return
17      * @throws java.lang.reflect.InvocationTargetException
18      */
19     public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
20         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
21         try {
22             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
23         } catch (Throwable e) {
24             throw new IllegalArgumentException(e);
25         }
26         try {
27             if ("sayHello".equals(n) && p.length == 1) {
28                 return ($w) w.sayHello((java.lang.String) v[0]);
29             }
30         } catch (Throwable e) {
31             throw new java.lang.reflect.InvocationTargetException(e);
32         }
33         throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
34     }
35 }
复制代码

这里距执行到了DemoServiceImpl的sayHello(String name)方法。之后将返回结果封装为RpcResult并返回,一直返回到HeaderExchangeHandler的received(Channel channel, Object message)

复制代码
 1     public void received(Channel channel, Object message) throws RemotingException {
 2         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
 3         ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
 4         try {
 5             if (message instanceof Request) {
 6                 // handle request.
 7                 Request request = (Request) message;
 8                 if (request.isEvent()) {
 9                     handlerEvent(channel, request);
10                 } else {
11                     if (request.isTwoWay()) {
12                         Response response = handleRequest(exchangeChannel, request);
13                         channel.send(response);
14                     } else {
15                         handler.received(exchangeChannel, request.getData());
16                     }
17                 }
18             } else if (message instanceof Response) {
19                 handleResponse(channel, (Response) message);
20             } else if (message instanceof String) {
21                 if (isClientSide(channel)) {
22                     Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
23                     logger.error(e.getMessage(), e);
24                 } else {
25                     String echo = handler.telnet(channel, (String) message);
26                     if (echo != null && echo.length() > 0) {
27                         channel.send(echo);
28                     }
29                 }
30             } else {
31                 handler.received(exchangeChannel, message);
32             }
33         } finally {
34             HeaderExchangeChannel.removeChannelIfDisconnected(channel);
35         }
36     }
复制代码

之后将响应结果返回给客户端,这里的channel是NettyChannel,执行NettyChannel的send方法,其调用NioAcceptedSocketChannel.write(Object message)将消息写会给客户端,结束!

一 总体流程

复制代码
客户端接收响应消息
NettyHandler.messageReceived(ChannelHandlerContext ctx, MessageEvent e)
-->MultiMessageHandler.received(Channel channel, Object message)
  -->HeartbeatHandler.received(Channel channel, Object message)
    -->AllChannelHandler.received(Channel channel, Object message)
      -->ExecutorService cexecutor = getExecutorService()
      -->cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message))
        -->ChannelEventRunnable.run()
          -->DecodeHandler.received(Channel channel, Object message)
            -->decode(Object message)
            -->HeaderExchangeHandler.received(Channel channel, Object message)
              -->handleResponse(Channel channel, Response response)
                -->DefaultFuture.received(channel, response)
                  -->doReceived(Response res)//异步转同步
复制代码

 

二 源码解析

在HeaderExchangeHandler.received(Channel channel, Object message)方法之前,与服务端接收请求消息一样,不再赘述。

HeaderExchangeHandler.received(Channel channel, Object message)

复制代码
 1     public void received(Channel channel, Object message) throws RemotingException {
 2         ...
 3         try {
 4             if (message instanceof Request) {
 5                 ...
 6             } else if (message instanceof Response) {
 7                 handleResponse(channel, (Response) message);
 8             } else if (message instanceof String) {
 9                 ...
10             } else {
11                 ...
12             }
13         } finally {
14             HeaderExchangeChannel.removeChannelIfDisconnected(channel);
15         }
16     }
17 
18     static void handleResponse(Channel channel, Response response) throws RemotingException {
19         if (response != null && !response.isHeartbeat()) {
20             DefaultFuture.received(channel, response);
21         }
22     }
复制代码

DefaultFuture.received(Channel channel, Response response)

复制代码
 1     private final long id;
 2     private final Request request;
 3     private final int timeout;
 4     private volatile Response response;
 5     private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
 6     private final Condition done = lock.newCondition();
 7 
 8     public static void received(Channel channel, Response response) {
 9         try {
10             DefaultFuture future = FUTURES.remove(response.getId());//删除元素并返回key=response.getId()的DefaultFuture
11             if (future != null) {
12                 future.doReceived(response);
13             } else {
14                 logger.warn("The timeout response finally returned at "
15                         + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
16                         + ", response " + response
17                         + (channel == null ? "" : ", channel: " + channel.getLocalAddress()
18                         + " -> " + channel.getRemoteAddress()));
19             }
20         } finally {
21             CHANNELS.remove(response.getId());
22         }
23     }
24 
25     private void doReceived(Response res) {
26         lock.lock();
27         try {
28             //设置response
29             response = res;
30             if (done != null) {
31                 //唤醒阻塞的线程
32                 done.signal();
33             }
34         } finally {
35             lock.unlock();
36         }
37         if (callback != null) {
38             invokeCallback(callback);
39         }
40     }
复制代码

这里比较难懂,笔者再给出客户端发出请求时的一段代码:HeaderExchangeChannel.request(Object request, int timeout)

复制代码
 1     public ResponseFuture request(Object request, int timeout) throws RemotingException {
 2         if (closed) {
 3             throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
 4         }
 5         // create request.
 6         Request req = new Request();
 7         req.setVersion("2.0.0");
 8         req.setTwoWay(true);
 9         req.setData(request);
10         DefaultFuture future = new DefaultFuture(channel, req, timeout);
11         try {
12             channel.send(req);
13         } catch (RemotingException e) {
14             future.cancel();
15             throw e;
16         }
17         return future;
18     }
复制代码

netty是一个异步非阻塞的框架,所以当执行channel.send(req);的时候,当其内部执行到netty发送消息时,不会等待结果,直接返回。为了实现“异步转为同步”,使用了DefaultFuture这个辅助类,

在HeaderExchangeChannel.request(Object request, int timeout),在还没有等到客户端的响应回来的时候,就直接将future返回了。返回给谁?再来看HeaderExchangeChannel.request(Object request, int timeout)的调用者。

1                   -->DubboInvoker.doInvoke(final Invocation invocation)
2                     //获取ExchangeClient进行消息的发送
3                     -->ReferenceCountExchangeClient.request(Object request, int timeout)
4                       -->HeaderExchangeClient.request(Object request, int timeout)
5                         -->HeaderExchangeChannel.request(Object request, int timeout)

DubboInvoker.doInvoke(final Invocation invocation)

复制代码
 1 protected Result doInvoke(final Invocation invocation) throws Throwable {
 2         RpcInvocation inv = (RpcInvocation) invocation;
 3         final String methodName = RpcUtils.getMethodName(invocation);
 4         inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
 5         inv.setAttachment(Constants.VERSION_KEY, version);
 6 
 7         ExchangeClient currentClient;
 8         if (clients.length == 1) {
 9             currentClient = clients[0];
10         } else {
11             currentClient = clients[index.getAndIncrement() % clients.length];
12         }
13         try {
14             boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);//是否异步
15             boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);//是否没有返回值
16             int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
17             if (isOneway) {
18                 boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
19                 currentClient.send(inv, isSent);
20                 RpcContext.getContext().setFuture(null);
21                 return new RpcResult();
22             } else if (isAsync) {
23                 ResponseFuture future = currentClient.request(inv, timeout);
24                 RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
25                 return new RpcResult();
26             } else {
27                 RpcContext.getContext().setFuture(null);
28                 return (Result) currentClient.request(inv, timeout).get();
29             }
30         } catch (TimeoutException e) {
31             throw new RpcException(...);
32         } catch (RemotingException e) {
33             throw new RpcException(...);
34         }
35     }
复制代码

其中currentClient.request(inv, timeout)返回值是ResponseFuture,DefaultFuture是ResponseFuture的实现类,实际上这里返回的就是DefaultFuture实例,而该实例就是HeaderExchangeChannel.request(Object request, int timeout)返回的那个future实例。之后调用DefaultFuture.get()。

复制代码
 1     public Object get() throws RemotingException {
 2         return get(timeout);
 3     }
 4 
 5     public Object get(int timeout) throws RemotingException {
 6         if (timeout <= 0) {
 7             timeout = Constants.DEFAULT_TIMEOUT;
 8         }
 9         if (!isDone()) {
10             long start = System.currentTimeMillis();
11             lock.lock();
12             try {
13                 while (!isDone()) {
14                     //Causes the current thread to wait until it is signalled or interrupted, or the specified waiting time elapses.
15                     done.await(timeout, TimeUnit.MILLISECONDS);
16                     if (isDone() || System.currentTimeMillis() - start > timeout) {
17                         break;
18                     }
19                 }
20             } catch (InterruptedException e) {
21                 throw new RuntimeException(e);
22             } finally {
23                 lock.unlock();
24             }
25             if (!isDone()) {
26                 throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
27             }
28         }
29         return returnFromResponse();
30     }
31 
32     public boolean isDone() {
33         return response != null;
34     }
复制代码

此处我们看到当响应response没有回来时,condition会执行await进行阻塞当前线程,直到被唤醒或被中断或阻塞时间到时了。当客户端接收到服务端的响应的时候,DefaultFuture.doReceived:

会先为response赋上返回值,之后执行condition的signal唤醒被阻塞的线程,get()方法就会释放锁,执行returnFromResponse(),返回值。

复制代码
 1     private Object returnFromResponse() throws RemotingException {
 2         Response res = response;
 3         if (res == null) {
 4             throw new IllegalStateException("response cannot be null");
 5         }
 6         if (res.getStatus() == Response.OK) {
 7             return res.getResult();
 8         }
 9         if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
10             throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
11         }
12         throw new RemotingException(channel, res.getErrorMessage());
13     }
复制代码

到现在其实还有一个问题?就是netty时异步非阻塞的,那么假设现在我发了1w个Request,后来返回来1w个Response,那么怎么对应Request和Response呢?如果对应不上,最起码的唤醒就会有问题。为了解决这个问题提,Request和Response中都有一个属性id。

在HeaderExchangeChannel.request(Object request, int timeout)中:

复制代码
 1         Request req = new Request();
 2         req.setVersion("2.0.0");
 3         req.setTwoWay(true);
 4         req.setData(request);
 5         DefaultFuture future = new DefaultFuture(channel, req, timeout);
 6         try {
 7             channel.send(req);
 8         } catch (RemotingException e) {
 9             future.cancel();
10             throw e;
11         }
12         return future;
复制代码

看一下Request的构造器:

复制代码
 1     private static final AtomicLong INVOKE_ID = new AtomicLong(0);
 2     private final long mId;
 3 
 4     public Request() {
 5         mId = newId();
 6     }
 7 
 8     private static long newId() {
 9         // getAndIncrement()增长到MAX_VALUE时,再增长会变为MIN_VALUE,负数也可以做为ID
10         return INVOKE_ID.getAndIncrement();
11     }
复制代码

看一下DefaultFuture的构造器:

复制代码
 1     private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
 2     private final long id;
 3     private final Request request;
 4     private volatile Response response;
 5 
 6     public DefaultFuture(Channel channel, Request request, int timeout) {
 7         ...
 8         this.request = request;
 9         this.id = request.getId();
10         ...
11         FUTURES.put(id, this);
12         ...
13     }
复制代码

再来看一下响应。

HeaderExchangeHandler.handleRequest(ExchangeChannel channel, Request req)

复制代码
 1     Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
 2         Response res = new Response(req.getId(), req.getVersion());
 3         ...
 4         Object msg = req.getData();
 5         try {
 6             // handle data.
 7             Object result = handler.reply(channel, msg);
 8             res.setStatus(Response.OK);
 9             res.setResult(result);
10         } catch (Throwable e) {
11             res.setStatus(Response.SERVICE_ERROR);
12             res.setErrorMessage(StringUtils.toString(e));
13         }
14         return res;
15     }
复制代码

来看一下Response的构造器:

1     private long mId = 0;
2 
3     public Response(long id, String version) {
4         mId = id;
5         mVersion = version;
6     }

这里response的id的值时request的id。最后来看一下服务端接收后的处理:

DefaultFuture.received(Channel channel, Response response)

复制代码
 1     public static void received(Channel channel, Response response) {
 2         try {
 3             DefaultFuture future = FUTURES.remove(response.getId());//删除元素并返回key=response.getId()的DefaultFuture
 4             if (future != null) {
 5                 future.doReceived(response);
 6             } else {
 7                ...
 8             }
 9         } finally {
10             CHANNELS.remove(response.getId());
11         }
12     }
复制代码

9.1 客户端发起请求源码9.2 服务端接收请求消息并发送响应消息源码9.3 客户端接收响应信息(异步转同步的实现) 分析了dubbo同步调用的源码,现在来看一下dubbo异步调用。

一、使用方式

服务提供方不变,调用方代码如下:

1     <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
2         <dubbo:method name="sayHello" async="true" timeout="60000"/>
3         <dubbo:method name="sayBye" async="true" timeout="60000"/>
4     </dubbo:reference>

配置里添加<dubbo:method name="xxx" async="true"/>,表示单个方法xxx使用异步方式;如果demoService下的所有方法都使用异步,直接配置为<dubbo:reference async="true"/>。

复制代码
 1     public static void main(String[] args) throws Exception {
 2         //Prevent to get IPV6 address,this way only work in debug mode
 3         //But you can pass use -Djava.net.preferIPv4Stack=true,then it work well whether in debug mode or not
 4         System.setProperty("java.net.preferIPv4Stack", "true");
 5 
 6         asyncFuture2();
 7     }
 8 
 9     public static void asyncFuture1() throws ExecutionException, InterruptedException {
10         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
11         context.start();
12         DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy
13 
14         long start = System.currentTimeMillis();
15 
16         demoService.sayHello("zhangsan");
17         Future<String> helloFuture = RpcContext.getContext().getFuture();
18 
19         demoService.sayBye("lisi");
20         Future<String> byeFuture = RpcContext.getContext().getFuture();
21 
22         final String helloStr = helloFuture.get();//消耗5s
23         final String byeStr = byeFuture.get();//消耗8s
24 
25         System.out.println(helloStr + " -- " + byeStr + " ,cost:" + (System.currentTimeMillis()-start));//总消耗8s
26     }
27 
28     public static void asyncFuture2() throws ExecutionException, InterruptedException {
29         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
30         context.start();
31         DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy
32 
33         long start = System.currentTimeMillis();
34 
35         Future<String> helloFuture = RpcContext.getContext().asyncCall(()-> demoService.sayHello("zhangsan"));
36         Future<String> byeFuture = RpcContext.getContext().asyncCall(()->demoService.sayBye("lisi"));
37 
38         final String helloStr = helloFuture.get();//消耗5s
39         final String byeStr = byeFuture.get();//消耗8s
40 
41         System.out.println(helloStr + " -- " + byeStr + " ,cost:" + (System.currentTimeMillis()-start));//总消耗8s
42     }
复制代码

Consumer启动主类。其中asyncFuture2()方法是推荐用法,注意Callable(asyncCall方法的入参)只是一个任务task,不会新建线程;所以asyncFuture2()和asyncFuture1()相似,资源占用相同,都是用一根线程进行异步操作的。

 

二、asyncFuture1()源码解析

先来看asyncFuture1(),总体步骤:

  • demoService.sayHello("zhangsan"); 创建一个Future对象,存入当前线程的上下文中
  • Future<String> helloFuture = RpcContext.getContext().getFuture(); 从当前线程的上下文中获取第一步存入的Future对象
  • final String helloStr = helloFuture.get(); 阻塞等待,从Future中获取结果

代码主要执行流(代码详细执行流看文章开头的三篇博客):

1、demoService.sayHello("zhangsan"); 

-->FutureFilter.invoke(final Invoker<?> invoker, final Invocation invocation)
   -->DubboInvoker.doInvoke(final Invocation invocation)

FutureFilter:

复制代码
 1     public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
 2         final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
 3 
 4         fireInvokeCallback(invoker, invocation);
 5         // need to configure if there's return value before the invocation in order to help invoker to judge if it's
 6         // necessary to return future.
 7         Result result = invoker.invoke(invocation);
 8         if (isAsync) {
 9             asyncCallback(invoker, invocation);
10         } else {
11             syncCallback(invoker, invocation, result);
12         }
13         return result;
14     }
复制代码

对于如上异步操作(asyncFuture1()和asyncFuture2()),FutureFilter没起任何作用,该Filter主要会用在事件通知中,后续再说。

DubboInvoker.doInvoke(final Invocation invocation):

复制代码
 1     protected Result doInvoke(final Invocation invocation) throws Throwable {
 2         RpcInvocation inv = (RpcInvocation) invocation;
 3         final String methodName = RpcUtils.getMethodName(invocation);
 4         inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
 5         inv.setAttachment(Constants.VERSION_KEY, version);
 6 
 7         ExchangeClient currentClient;
 8         if (clients.length == 1) {
 9             currentClient = clients[0];
10         } else {
11             currentClient = clients[index.getAndIncrement() % clients.length];
12         }
13         try {
14             boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
15             boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
16             int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
17             if (isOneway) { //无返回值
18                 boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
19                 currentClient.send(inv, isSent);
20                 RpcContext.getContext().setFuture(null);
21                 return new RpcResult();
22             } else if (isAsync) { //异步有返回值
23                 ResponseFuture future = currentClient.request(inv, timeout);
24                 RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
25                 return new RpcResult();
26             } else { //同步有返回值
27                 RpcContext.getContext().setFuture(null);
28                 return (Result) currentClient.request(inv, timeout).get();
29             }
30         } catch (TimeoutException e) {
31             throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
32         } catch (RemotingException e) {
33             throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
34         }
35     }
复制代码

模式:

  • 如果是isOneway(不需要返回值),不管同步还是异步,请求直接发出,不会创建Future,直接返回RpcResult空对象。
  • 如果是isAsync(异步),则
    • 先创建ResponseFuture对象,之后使用FutureAdapter包装该ResponseFuture对象;(创建ResponseFuture对象与同步的代码相同,最后得到的是一个DefaultFuture对象)
    • 然后将该FutureAdapter对象设入当前线程的上下文中RpcContext.getContext();
    • 最后返回空的RpcResult
  • 如果是同步,则先创建ResponseFuture对象,之后直接调用其get()方法进行阻塞调用(见文章开头的三篇文章)

简单来看一下FutureAdapter:

复制代码
 1 public class FutureAdapter<V> implements Future<V> {
 2 
 3     private final ResponseFuture future;
 4 
 5     public FutureAdapter(ResponseFuture future) {
 6         this.future = future;
 7     }
 8 
 9     public ResponseFuture getFuture() {
10         return future;
11     }
12 
13     public boolean cancel(boolean mayInterruptIfRunning) {
14         return false;
15     }
16 
17     public boolean isCancelled() {
18         return false;
19     }
20 
21     public boolean isDone() {
22         return future.isDone();
23     }
24 
25     @SuppressWarnings("unchecked")
26     public V get() throws InterruptedException, ExecutionException {
27         try {
28             return (V) (((Result) future.get()).recreate());
29         } catch (RemotingException e) {
30             throw new ExecutionException(e.getMessage(), e);
31         } catch (Throwable e) {
32             throw new RpcException(e);
33         }
34     }
35 
36     @SuppressWarnings("unchecked")
37     public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
38         int timeoutInMillis = (int) unit.convert(timeout, TimeUnit.MILLISECONDS);
39         try {
40             return (V) (((Result) future.get(timeoutInMillis)).recreate());
41         } catch (com.alibaba.dubbo.remoting.TimeoutException e) {
42             throw new TimeoutException(StringUtils.toString(e));
43         } catch (RemotingException e) {
44             throw new ExecutionException(e.getMessage(), e);
45         } catch (Throwable e) {
46             throw new RpcException(e);
47         }
48     }
49 }
复制代码

最后,回头看一下FutureFilter:

复制代码
 1     public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
 2         final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
 3 
 4         fireInvokeCallback(invoker, invocation);
 5         // need to configure if there's return value before the invocation in order to help invoker to judge if it's
 6         // necessary to return future.
 7         Result result = invoker.invoke(invocation);
 8         if (isAsync) {
 9             asyncCallback(invoker, invocation);
10         } else {
11             syncCallback(invoker, invocation, result);
12         }
13         return result;
14     }
复制代码
复制代码
 1     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 2         Future<?> f = RpcContext.getContext().getFuture();
 3         if (f instanceof FutureAdapter) {
 4             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 5             future.setCallback(new ResponseCallback() {
 6                 public void done(Object rpcResult) {
 7                     if (rpcResult == null) {
 8                         logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
 9                         return;
10                     }
11                     ///must be rpcResult
12                     if (!(rpcResult instanceof Result)) {
13                         logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
14                         return;
15                     }
16                     Result result = (Result) rpcResult;
17                     if (result.hasException()) {
18                         fireThrowCallback(invoker, invocation, result.getException());
19                     } else {
20                         fireReturnCallback(invoker, invocation, result.getValue());
21                     }
22                 }
23 
24                 public void caught(Throwable exception) {
25                     fireThrowCallback(invoker, invocation, exception);
26                 }
27             });
28         }
29     }
复制代码

这里的future对象时之前创建好的DefaultFuture对象。

复制代码
 1     private volatile Response response;
 2     private volatile ResponseCallback callback;
 3 
 4     public boolean isDone() {
 5         return response != null;
 6     }
 7 
 8     public void setCallback(ResponseCallback callback) {
 9         if (isDone()) {
10             invokeCallback(callback);
11         } else {
12             boolean isdone = false;
13             lock.lock();
14             try {
15                 if (!isDone()) {
16                     this.callback = callback;
17                 } else {
18                     isdone = true;
19                 }
20             } finally {
21                 lock.unlock();
22             }
23             if (isdone) {
24                 invokeCallback(callback);
25             }
26         }
27     }
复制代码

这里判断响应是否已经返回了,如果返回了,直接执行invokeCallback(callback),否则将传入的ResponseCallback对象赋值给callback对象。

 

2、Future<String> helloFuture = RpcContext.getContext().getFuture(); 

RpcContext:

复制代码
 1     private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
 2         @Override
 3         protected RpcContext initialValue() {
 4             return new RpcContext();
 5         }
 6     };
 7 
 8     private Future<?> future;
 9 
10     public static RpcContext getContext() {
11         return LOCAL.get();
12     }
13 
14     public <T> Future<T> getFuture() {
15         return (Future<T>) future;
16     }
复制代码

从当前线程上下文中获取之前存进去的FutureAdapter对象。

 

3、final String helloStr = helloFuture.get(); 

helloFuture是上述的FutureAdapter对象,其get()调用的是内部的DefaultFuture的get(),该方法与同步调用时相同,源码分析见文章开头的三篇文章。

复制代码
1     public V get() throws InterruptedException, ExecutionException {
2         try {
3             return (V) (((Result) future.get()).recreate());
4         } catch (RemotingException e) {
5             throw new ExecutionException(e.getMessage(), e);
6         } catch (Throwable e) {
7             throw new RpcException(e);
8         }
9     }
复制代码

get方法的超时设置除了直接在xml中配置之外,还可以在代码中手动执行(优先级高) 

1 final String helloStr2 = helloFuture.get(7000, TimeUnit.MILLISECONDS);

 

三、asyncFuture2()源码解析

下面来看一下asyncFuture2()源码:

1、Future<String> helloFuture = RpcContext.getContext().asyncCall(()-> demoService.sayHello("zhangsan"));

复制代码
 1     public <T> Future<T> asyncCall(Callable<T> callable) {
 2         try {
 3             try {
 4                 setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
 5                 // 1 执行传入的任务(此处创建FutureAdapter对象,并且设置到当前线程的RpcContext的future对象中)
 6                 final T o = callable.call();
 7                 //local invoke will return directly
 8                 if (o != null) {
 9                     FutureTask<T> f = new FutureTask<T>(new Callable<T>() {
10                         public T call() throws Exception {
11                             return o;
12                         }
13                     });
14                     f.run();
15                     return f;
16                 } else {
17 
18                 }
19             } catch (Exception e) {
20                 throw new RpcException(e);
21             } finally {
22                 removeAttachment(Constants.ASYNC_KEY);
23             }
24         } catch (final RpcException e) {
25             return new Future<T>() {
26                 public boolean cancel(boolean mayInterruptIfRunning) {
27                     return false;
28                 }
29 
30                 public boolean isCancelled() {
31                     return false;
32                 }
33 
34                 public boolean isDone() {
35                     return true;
36                 }
37 
38                 public T get() throws InterruptedException, ExecutionException {
39                     throw new ExecutionException(e.getCause());
40                 }
41 
42                 public T get(long timeout, TimeUnit unit)
43                         throws InterruptedException, ExecutionException,
44                         TimeoutException {
45                     return get();
46                 }
47             };
48         }
49         // 2 从当前线程的RpcContext中获取future对象
50         return ((Future<T>) getContext().getFuture());
51     }
复制代码

这里外层的catch的作用是什么?没搞清楚 https://github.com/alibaba/dubbo/issues/1346

2、final String helloStr = helloFuture.get();

与同步相同。

总结:dubbo异步与同步的差别:

  • 同步:创建DefaultFuture之后,直接get阻塞等待;
  • 异步:创建DefaultFuture之后,使用FutureAdapter进行包装,之后设置到当前线程的RpcContext中;后续用户在合适的时候自己从RpcContext获取future,之后get。

dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html

一、使用方式

两个服务:

  • DemoService:真正要调用的服务
  • Notify:事件通知服务(用在consumer端)

provider

1 package com.alibaba.dubbo.demo;
2 
3 public interface DemoService {
4     String sayHello(String name);
5 }
复制代码
1 public class DemoServiceImpl implements DemoService {
2     @Override
3     public String sayHello(String name) {
4         throw new RpcException("ex, param: " + name);//测试onthrow方法
5 //        return "Hello " + name;//测试onreturn方法
6     }
7 }
复制代码

consumer

通知服务:Notify

复制代码
1 package com.alibaba.dubbo.demo.consumer.eventnotify;
2 
3 public interface Notify {
4     void oninvoke(String name); // 调用之前
5     void onreturnWithoutParam(String result); // 调用之后
6     void onreturn(String result, String name); // 调用之后
7     void onthrow(Throwable ex, String name);  // 出现异常
8 }
复制代码
复制代码
 1 package com.alibaba.dubbo.demo.consumer.eventnotify;
 2 
 3 public class NotifyService implements Notify {
 4     @Override
 5     public void oninvoke(String name) {
 6         System.out.println("======oninvoke======, param: " + name);
 7     }
 8 
 9     @Override
10     public void onreturnWithoutParam(String result) {
11         System.out.println("======onreturn======, result: " + result);
12     }
13 
14     @Override
15     public void onreturn(String result, String name) {
16         System.out.println("======onreturn======, param: " + name + ", result: " + result);
17     }
18 
19     @Override
20     public void onthrow(Throwable ex, String name) {
21         System.out.println("======onthrow======, param: " + name + ", exception: " + ex.getMessage());
22     }
23 }
复制代码

xml配置:

1     <bean id="notifyService"  class="com.alibaba.dubbo.demo.consumer.eventnotify.NotifyService"/>
2     <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
3         <dubbo:method name="sayHello" timeout="60000" oninvoke="notifyService.oninvoke" onreturn="notifyService.onreturnWithoutParam" onthrow="notifyService.onthrow"/>
4     </dubbo:reference>

之后就可以运行Consumer启动类,之后调用demoService.sayHello(String name)了。

注意:

  • oninvoke方法:
    • 必须具有与真实的被调用方法sayHello相同的入参列表:例如,oninvoke(String name)
  • onreturn方法:
    • 至少要有一个入参且第一个入参必须与sayHello的返回类型相同,接收返回结果:例如,onreturnWithoutParam(String result)
    • 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收sayHello入参的:例如, onreturn(String result, String name)
  • onthrow方法:
    • 至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果;例如,onthrow(Throwable ex)
    • 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收sayHello入参的:例如,onthrow(Throwable ex, String name)
  • 如果是consumer在调用provider的过程中,出现异常时不会走onthrow方法的,onthrow方法只会在provider返回的RpcResult中含有Exception对象时,才会执行。(dubbo中下层服务的Exception会被放在响应RpcResult的exception对象中传递给上层服务)

 

二、源码解析

整个事件通知的逻辑都在FutureFilter中,来看一下源码:

复制代码
  1 /**
  2  * EventFilter
  3  */
  4 @Activate(group = Constants.CONSUMER)
  5 public class FutureFilter implements Filter {
  6 
  7     protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
  8 
  9     public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
 10         final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
 11 
 12         //1 调用服务之前:执行xxxService.oninvoke方法
 13         fireInvokeCallback(invoker, invocation);
 14         //2 调用服务
 15         Result result = invoker.invoke(invocation);
 16         //3 调用服务之后
 17         if (isAsync) {
 18             asyncCallback(invoker, invocation);
 19         } else {
 20             syncCallback(invoker, invocation, result);
 21         }
 22         //4 返回调用结果
 23         return result;
 24     }
 25 
 26     private void syncCallback(final Invoker<?> invoker, final Invocation invocation, final Result result) {
 27         if (result.hasException()) {
 28             //3.1 调用服务之后:如果返回结果异常信息(注意:如果是consumer自己throw的异常,会在2的时候直接抛走,不会走到这里),直接执行xxxService.onthrow方法
 29             fireThrowCallback(invoker, invocation, result.getException());
 30         } else {
 31             //3.2 调用服务之后:如果返回值正常,执行xxxService.onreturn方法
 32             fireReturnCallback(invoker, invocation, result.getValue());
 33         }
 34     }
 35 
 36     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 37         Future<?> f = RpcContext.getContext().getFuture();
 38         if (f instanceof FutureAdapter) {
 39             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 40             // 3.1 调用服务之后:设置回调ResponseCallback对象到DefaultFuture中,当provider返回响应时,执行DefaultFuture.doReceived方法,该方法会调用ResponseCallback对象的done或者caught方法
 41             future.setCallback(new ResponseCallback() {
 42                 public void done(Object rpcResult) {
 43                     if (rpcResult == null) {
 44                         logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
 45                         return;
 46                     }
 47                     ///must be rpcResult
 48                     if (!(rpcResult instanceof Result)) {
 49                         logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
 50                         return;
 51                     }
 52                     Result result = (Result) rpcResult;
 53                     if (result.hasException()) {
 54                         fireThrowCallback(invoker, invocation, result.getException());
 55                     } else {
 56                         fireReturnCallback(invoker, invocation, result.getValue());
 57                     }
 58                 }
 59 
 60                 public void caught(Throwable exception) {
 61                     fireThrowCallback(invoker, invocation, exception);
 62                 }
 63             });
 64         }
 65     }
 66 
 67     /**
 68      * 反射执行xxxService.oninvoke方法:必须具有与真实的被调用方法sayHello相同的入参列表。
 69      */
 70     private void fireInvokeCallback(final Invoker<?> invoker, final Invocation invocation) {
 71         final Method onInvokeMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_METHOD_KEY));
 72         final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_INSTANCE_KEY));
 73 
 74         if (onInvokeMethod == null && onInvokeInst == null) {
 75             return;
 76         }
 77         if (onInvokeMethod == null || onInvokeInst == null) {
 78             throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onreturn callback config , but no such " + (onInvokeMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
 79         }
 80         if (onInvokeMethod != null && !onInvokeMethod.isAccessible()) {
 81             onInvokeMethod.setAccessible(true);
 82         }
 83         // 获取真实方法sayHello传入的参数
 84         Object[] params = invocation.getArguments();
 85         try {
 86             onInvokeMethod.invoke(onInvokeInst, params);
 87         } catch (InvocationTargetException e) {
 88             fireThrowCallback(invoker, invocation, e.getTargetException());
 89         } catch (Throwable e) {
 90             fireThrowCallback(invoker, invocation, e);
 91         }
 92     }
 93 
 94     /**
 95      * 反射执行xxxService.onreturn方法:至少要有一个入参,接收返回结果
 96      */
 97     private void fireReturnCallback(final Invoker<?> invoker, final Invocation invocation, final Object result) {
 98         final Method onReturnMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_METHOD_KEY));
 99         final Object onReturnInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_INSTANCE_KEY));
100 
101         //not set onreturn callback
102         if (onReturnMethod == null && onReturnInst == null) {
103             return;
104         }
105 
106         if (onReturnMethod == null || onReturnInst == null) {
107             throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onreturn callback config , but no such " + (onReturnMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
108         }
109         if (onReturnMethod != null && !onReturnMethod.isAccessible()) {
110             onReturnMethod.setAccessible(true);
111         }
112 
113         Object[] args = invocation.getArguments();
114         Object[] params;
115         Class<?>[] rParaTypes = onReturnMethod.getParameterTypes();
116         if (rParaTypes.length > 1) {
117             // onreturn(xx, Object[]) 两个参数:第一个参数与真实方法sayHello方法返回结果类型相同,第二个接收所有的真实请求参数
118             if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) {
119                 params = new Object[2];
120                 params[0] = result; // 真实方法的执行结果
121                 params[1] = args;   // 真实方法sayHello传入的参数
122             // onreturn(xx, Object... args) 多个参数:第一个参数与真实方法sayHello方法返回结果类型相同,后边几个接收所有的真实请求参数
123             } else {
124                 params = new Object[args.length + 1];
125                 params[0] = result; // 真实方法的执行结果
126                 System.arraycopy(args, 0, params, 1, args.length);
127             }
128         } else {
129             // onreturn(xx) 只有一个参数:接收返回执行结果
130             params = new Object[]{result}; // 执行结果
131         }
132         try {
133             onReturnMethod.invoke(onReturnInst, params);
134         } catch (InvocationTargetException e) {
135             fireThrowCallback(invoker, invocation, e.getTargetException());
136         } catch (Throwable e) {
137             fireThrowCallback(invoker, invocation, e);
138         }
139     }
140 
141     /**
142      * 反射执行xxxService.onthrow方法:至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果
143      */
144     private void fireThrowCallback(final Invoker<?> invoker, final Invocation invocation, final Throwable exception) {
145         final Method onthrowMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_METHOD_KEY));
146         final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_INSTANCE_KEY));
147 
148         //onthrow callback not configured
149         if (onthrowMethod == null && onthrowInst == null) {
150             return;
151         }
152         if (onthrowMethod == null || onthrowInst == null) {
153             throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onthrow callback config , but no such " + (onthrowMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
154         }
155         if (onthrowMethod != null && !onthrowMethod.isAccessible()) {
156             onthrowMethod.setAccessible(true);
157         }
158         Class<?>[] rParaTypes = onthrowMethod.getParameterTypes();
159         if (rParaTypes[0].isAssignableFrom(exception.getClass())) {
160             try {
161                 Object[] args = invocation.getArguments();
162                 Object[] params;
163 
164                 if (rParaTypes.length > 1) {
165                     // onthrow(xx, Object[]) 两个参数:第一个参数接收exception,第二个接收所有的真实请求参数
166                     if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) {
167                         params = new Object[2];
168                         params[0] = exception;
169                         params[1] = args;
170                     // onthrow(xx, Object... args) 多个参数:第一个参数接收exception,后边几个接收所有的真实请求参数
171                     } else {
172                         params = new Object[args.length + 1];
173                         params[0] = exception;
174                         System.arraycopy(args, 0, params, 1, args.length);
175                     }
176                 } else {
177                     // onthrow(xx) 只有一个参数:接收exception
178                     params = new Object[]{exception};
179                 }
180                 onthrowMethod.invoke(onthrowInst, params);
181             } catch (Throwable e) {
182                 logger.error(invocation.getMethodName() + ".call back method invoke error . callback method :" + onthrowMethod + ", url:" + invoker.getUrl(), e);
183             }
184         } else {
185             logger.error(invocation.getMethodName() + ".call back method invoke error . callback method :" + onthrowMethod + ", url:" + invoker.getUrl(), exception);
186         }
187     }
188 }
复制代码

从@Activate(group = Constants.CONSUMER)来看FutureFilter只用在consumer端;不管是同步调用还是异步调用,都会走FutureFilter。

原理:

  • 首先走oninvoke(String name)方法;
  • 然后走sayHello(String name)
  • 最后根据同步还是异步分别走不同的逻辑。 

其中同步很简单,看sayHello(String name)的返回结果RpcResult中是否有exception对象,如果有,执行onthrow(Throwable ex, String name);如果没有执行onreturnWithoutParam(String result)。

异步的操作:由于不知道provider什么时候回执行完毕,所以要添加回调等待provider端返回结果后,再执行onthrow(Throwable ex, String name)或者onreturnWithoutParam(String result),这种模式很重要,这是统计异步方法调用时间的一种非常好的模式

重点看一下异步!

 

三、异步回调模式

复制代码
 1     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 2         Future<?> f = RpcContext.getContext().getFuture();
 3         if (f instanceof FutureAdapter) {
 4             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 5             // 3.1 调用服务之后:设置回调ResponseCallback对象到DefaultFuture中,当provider返回响应时,执行DefaultFuture.doReceived方法,该方法会调用ResponseCallback对象的done或者caught方法
 6             future.setCallback(new ResponseCallback() {
 7                 public void done(Object rpcResult) {
 8                     if (rpcResult == null) {
 9                         logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
10                         return;
11                     }
12                     ///must be rpcResult
13                     if (!(rpcResult instanceof Result)) {
14                         logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
15                         return;
16                     }
17                     Result result = (Result) rpcResult;
18                     if (result.hasException()) {
19                         fireThrowCallback(invoker, invocation, result.getException());
20                     } else {
21                         fireReturnCallback(invoker, invocation, result.getValue());
22                     }
23                 }
24 
25                 public void caught(Throwable exception) {
26                     fireThrowCallback(invoker, invocation, exception);
27                 }
28             });
29         }
30     }
复制代码

上述的future对象是DefaultFuture,这里首先new了一个ResponseCallback回调函数,设置到了DefaultFuture的ResponseCallback callback属性中。来看一下DefaultFuture类:

复制代码
 1     private volatile Response response;
 2     private volatile ResponseCallback callback;
 3 
 4     public boolean isDone() {
 5         return response != null;
 6     }
 7 
 8     public void setCallback(ResponseCallback callback) {
 9         if (isDone()) {
10             invokeCallback(callback);
11         } else {
12             boolean isdone = false;
13             lock.lock();
14             try {
15                 if (!isDone()) {
16                     this.callback = callback;
17                 } else {
18                     isdone = true;
19                 }
20             } finally {
21                 lock.unlock();
22             }
23             if (isdone) {
24                 invokeCallback(callback);
25             }
26         }
27     }
复制代码
复制代码
 1     private void invokeCallback(ResponseCallback c) {
 2         ResponseCallback callbackCopy = c;
 3         if (callbackCopy == null) {
 4             throw new NullPointerException("callback cannot be null.");
 5         }
 6         c = null;
 7         Response res = response;
 8         if (res == null) {
 9             throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
10         }
11 
12         if (res.getStatus() == Response.OK) {
13             try {
14                 // 返回正常,回调ResponseCallback回调函数的done方法
15                 callbackCopy.done(res.getResult());
16             } catch (Exception e) {
17                 logger.error("callback invoke error .reasult:" + res.getResult() + ",url:" + channel.getUrl(), e);
18             }
19         } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
20             try {
21                 TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
22                 // 如果超时,回调ResponseCallback回调函数的caught方法
23                 callbackCopy.caught(te);
24             } catch (Exception e) {
25                 logger.error("callback invoke error ,url:" + channel.getUrl(), e);
26             }
27         } else {
28             try {
29                 RuntimeException re = new RuntimeException(res.getErrorMessage());
30                 // 其他异常,回调ResponseCallback回调函数的caught方法
31                 callbackCopy.caught(re);
32             } catch (Exception e) {
33                 logger.error("callback invoke error ,url:" + channel.getUrl(), e);
34             }
35         }
36     }
复制代码

从setCallback(ResponseCallback callback),如果此时provider端已经返回了响应(response!=null),则直接执行ResponseCallback回调函数中的done方法或者caught方法;否则,将上边创建的ResponseCallback实例赋值给DefaultFuture的ResponseCallback callback属性中。那么之后会在什么时候执行回调函数的方法呢?当consumer接收到provider的响应的时候!

复制代码
 1     public static void received(Channel channel, Response response) {
 2         try {
 3             DefaultFuture future = FUTURES.remove(response.getId());
 4             if (future != null) {
 5                 future.doReceived(response);
 6             } else {
 7                 logger.warn("The timeout response finally returned at "
 8                         + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
 9                         + ", response " + response
10                         + (channel == null ? "" : ", channel: " + channel.getLocalAddress()
11                         + " -> " + channel.getRemoteAddress()));
12             }
13         } finally {
14             CHANNELS.remove(response.getId());
15         }
16     }
17 
18     private void doReceived(Response res) {
19         lock.lock();
20         try {
21             response = res;
22             if (done != null) {
23                 done.signal();
24             }
25         } finally {
26             lock.unlock();
27         }
28         // 调用回调函数
29         if (callback != null) {
30             invokeCallback(callback);
31         }
32     }
复制代码

当provider返回响应时,会调用DefaultFuture.received(Channel channel, Response response)方法(9.3 客户端接收响应信息(异步转同步的实现)),此时会执行回调函数。事件通知的源码就分析完了!最后看一个回调模式的使用场景:统计异步方法的调用时间。

复制代码
 1     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 2         Future<?> f = RpcContext.getContext().getFuture();
 3         final long start = System.currentTimeMillis();
 4         if (f instanceof FutureAdapter) {
 5             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 6             future.setCallback(new ResponseCallback() {
 7                 public void done(Object rpcResult) {
 8                     long cost = System.currentTimeMillis() - start;
 9                 }
10             });
11         }
12     }
复制代码

上边的代码只是一个形式,实际上start时间需要在调用sayHello方法之前进行记录。

dubbo的心跳机制:

  • 目的:检测provider与consumer之间的connection连接是不是还连接着,如果连接断了,需要作出相应的处理。
  • 原理:
    • provider:dubbo的心跳默认是在heartbeat(默认是60s)内如果没有接收到消息,就会发送心跳消息,如果连着3次(180s)没有收到心跳响应,provider会关闭channel。
    • consumer:dubbo的心跳默认是在60s内如果没有接收到消息,就会发送心跳消息,如果连着3次(180s)没有收到心跳响应,consumer会进行重连。

来看源码调用链。先看provider端。

 

一、provider端心跳机制

复制代码
              -->openServer(URL url)
                 url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.10.10.10&bind.port=20880&default.server=netty4&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=21999&qos.port=22222&side=provider&timestamp=1520660491836
                -->createServer(URL url)
                    -->HeaderExchanger.bind(URL url, ExchangeHandler handler)
                       url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.10.10.10&bind.port=20880&channel.readonly.sent=true&codec=dubbo&default.server=netty4&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=21999&qos.port=22222&side=provider&timestamp=1520660491836 handler:DubboProtocol.requestHandler
                      -->new DecodeHandler(new HeaderExchangeHandler(handler)))
                        -->NettyTransporter.bind(URL url, ChannelHandler listener)
                           listener:上边的DecodeHandler实例
                          -->new NettyServer(URL url, ChannelHandler handler)
                            -->ChannelHandler.wrapInternal(ChannelHandler handler, URL url)
                               handler:上边的DecodeHandler实例
                            -->doOpen()//开启netty服务
                      -->new HeaderExchangeServer(Server server)
                         server:上述的NettyServer
                        -->startHeatbeatTimer()
复制代码

服务端在开启netty服务时, 在调用createServer时,会从url的parameters map中获取heartbeat配置,代码如下:

复制代码
 1     private ExchangeServer createServer(URL url) {
 2 
 3         ...
 4 
 5         url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
 6        
 7         ...
 8 
 9         ExchangeServer server;
10         try {
11             server = Exchangers.bind(url, requestHandler);
12         } catch (RemotingException e) {
13             throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
14         }
15 
16         ...
17 
18         return server;
19     }
复制代码

其中:int DEFAULT_HEARTBEAT = 60 * 1000,即当用户没有配置heartbeat(心跳时间)时,默认heartbeat=60s(即60s内没有接收到任何请求,就会发送心跳信息)。那么这个heartbeat到底该怎么配?

provider端:

1     <dubbo:service ...>
2         <dubbo:parameter key="heartbeat" value="3000"/>
3     </dubbo:service>

consumer端:

1     <dubbo:reference ...>
2         <dubbo:parameter key="heartbeat" value="3000"/>
3     </dubbo:reference>

再来看调用链,当执行到这一句。

1 ChannelHandler.wrapInternal(ChannelHandler handler, URL url)

会形成一个handler调用链,调用链如下:

复制代码
1 MultiMessageHandler
2 -->handler: HeartbeatHandler
3    -->handler: AllChannelHandler
4          -->url: providerUrl
5          -->executor: FixedExecutor
6          -->handler: DecodeHandler
7             -->handler: HeaderExchangeHandler
8                -->handler: ExchangeHandlerAdapter(DubboProtocol.requestHandler)
复制代码

这也是netty接收到请求后的处理链路,注意其中有一个HeartbeatHandler。

最后,执行new HeaderExchangeServer(Server server),来看源码:

复制代码
 1 public class HeaderExchangeServer implements ExchangeServer {
 2     /** 心跳定时器 */
 3     private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1,
 4             new NamedThreadFactory(
 5                     "dubbo-remoting-server-heartbeat",
 6                     true));
 7     /** NettyServer */
 8     private final Server server;
 9     // heartbeat timer
10     private ScheduledFuture<?> heatbeatTimer;
11     // heartbeat timeout (ms), default value is 0 , won't execute a heartbeat.
12     private int heartbeat;
13     private int heartbeatTimeout;
14     private AtomicBoolean closed = new AtomicBoolean(false);
15 
16     public HeaderExchangeServer(Server server) {
17         if (server == null) {
18             throw new IllegalArgumentException("server == null");
19         }
20         this.server = server;
21         this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
22         this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
23         if (heartbeatTimeout < heartbeat * 2) {
24             throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
25         }
26         startHeatbeatTimer();
27     }
28 
29     private void startHeatbeatTimer() {
30         stopHeartbeatTimer();
31         if (heartbeat > 0) {
32             heatbeatTimer = scheduled.scheduleWithFixedDelay(
33                     new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
34                         public Collection<Channel> getChannels() {
35                             return Collections.unmodifiableCollection(
36                                     HeaderExchangeServer.this.getChannels());
37                         }
38                     }, heartbeat, heartbeatTimeout),
39                     heartbeat, heartbeat, TimeUnit.MILLISECONDS);
40         }
41     }
42 
43     private void stopHeartbeatTimer() {
44         try {
45             ScheduledFuture<?> timer = heatbeatTimer;
46             if (timer != null && !timer.isCancelled()) {
47                 timer.cancel(true);
48             }
49         } catch (Throwable t) {
50             logger.warn(t.getMessage(), t);
51         } finally {
52             heatbeatTimer = null;
53         }
54     }
55 }
复制代码

创建HeaderExchangeServer时,初始化了heartbeat(心跳间隔时间)和heartbeatTimeout(心跳响应超时时间:即如果最终发送的心跳在这个时间内都没有返回,则做出响应的处理)。

  • heartbeat默认是0(从startHeatbeatTimer()方法可以看出只有heartbeat>0的情况下,才会发心跳,这里heartbeat如果从url的parameter map中获取不到,就是0,但是我们在前边看到dubbo会默认设置heartbeat=60s到parameter map中,所以此处的heartbeat=60s);
  • heartbeatTimeout:默认是heartbeat*3。(原因:假设一端发出一次heartbeatRequest,另一端在heartbeat内没有返回任何响应-包括正常请求响应和心跳响应,此时不能认为是连接断了,因为有可能还是网络抖动什么的导致了tcp包的重传超时等)
  • scheduled是一个含有一个线程的定时线程执行器(其中的线程名字为:"dubbo-remoting-server-heartbeat-thread-*")

之后启动心跳定时任务:

  • 首先如果原来有心跳定时任务,关闭原来的定时任务
  • 之后启动scheduled中的定时线程,从启动该线程开始,每隔heartbeat执行一次HeartBeatTask任务(第一次执行是在启动线程后heartbeat时)

来看一下HeartBeatTask的源码:

复制代码
 1 final class HeartBeatTask implements Runnable {
 2     // channel获取器:用于获取所有需要进行心跳检测的channel
 3     private ChannelProvider channelProvider;
 4     private int heartbeat;
 5     private int heartbeatTimeout;
 6 
 7     HeartBeatTask(ChannelProvider provider, int heartbeat, int heartbeatTimeout) {
 8         this.channelProvider = provider;
 9         this.heartbeat = heartbeat;
10         this.heartbeatTimeout = heartbeatTimeout;
11     }
12 
13     public void run() {
14         try {
15             long now = System.currentTimeMillis();
16             for (Channel channel : channelProvider.getChannels()) {
17                 if (channel.isClosed()) {
18                     continue;
19                 }
20                 try {
21                     // 获取最后一次读操作的时间
22                     Long lastRead = (Long) channel.getAttribute(
23                             HeaderExchangeHandler.KEY_READ_TIMESTAMP);
24                     // 获取最后一次写操作的时间
25                     Long lastWrite = (Long) channel.getAttribute(
26                             HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);
27                     // 如果在heartbeat内没有进行读操作或者写操作,则发送心跳请求
28                     if ((lastRead != null && now - lastRead > heartbeat)
29                             || (lastWrite != null && now - lastWrite > heartbeat)) {
30                         Request req = new Request();
31                         req.setVersion("2.0.0");
32                         req.setTwoWay(true);
33                         req.setEvent(Request.HEARTBEAT_EVENT);
34                         channel.send(req);
35                         if (logger.isDebugEnabled()) {
36                             logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress()
37                                     + ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms");
38                         }
39                     }
40                     //正常消息和心跳在heartbeatTimeout都没接收到
41                     if (lastRead != null && now - lastRead > heartbeatTimeout) {
42                         logger.warn("Close channel " + channel
43                                 + ", because heartbeat read idle time out: " + heartbeatTimeout + "ms");
44                         // consumer端进行重连
45                         if (channel instanceof Client) {
46                             try {
47                                 ((Client) channel).reconnect();
48                             } catch (Exception e) {
49                                 //do nothing
50                             }
51                         } else {// provider端关闭连接
52                             channel.close();
53                         }
54                     }
55                 } catch (Throwable t) {
56                     logger.warn("Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t);
57                 }
58             }
59         } catch (Throwable t) {
60             logger.warn("Unhandled exception when heartbeat, cause: " + t.getMessage(), t);
61         }
62     }
63 
64     interface ChannelProvider {
65         Collection<Channel> getChannels();
66     }
67 }            
复制代码

HeartBeatTask首先获取所有的channelProvider#getChannels获取所有需要心跳检测的channel,channelProvider实例是HeaderExchangeServer中在启动线程定时执行器的时候创建的内部类。

1                     new HeartBeatTask.ChannelProvider() {
2                         public Collection<Channel> getChannels() {
3                             return Collections.unmodifiableCollection(
4                                     HeaderExchangeServer.this.getChannels());
5                         }
6                     }

来看一下HeaderExchangeServer.this.getChannels():

复制代码
 1     public Collection<Channel> getChannels() {
 2         return (Collection) getExchangeChannels();
 3     }
 4 
 5     public Collection<ExchangeChannel> getExchangeChannels() {
 6         Collection<ExchangeChannel> exchangeChannels = new ArrayList<ExchangeChannel>();
 7         Collection<Channel> channels = server.getChannels();
 8         if (channels != null && channels.size() > 0) {
 9             for (Channel channel : channels) {
10                 exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel));
11             }
12         }
13         return exchangeChannels;
14     }
复制代码

实际上就是获取NettyServer中的全部channel连接。

 

获取到需要心跳检测的channel后,对每一个channel进行如下判断:

  • 如果在heartbeat内没有进行读操作或者写操作,则发送心跳请求
  • 如果正常消息和心跳在heartbeatTimeout都没接收到,consumer端会进行重连,provider端会关闭channel

这里比较关键的是lastRead和lastWrite的设置。先来看一下获取:

1 Long lastRead = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_READ_TIMESTAMP);
2 Long lastWrite = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);

说明有地方在设置这两个值到channel中。

从请求和响应处理来看,无论是请求还是响应都会按照这个顺序处理一遍。

复制代码
1 MultiMessageHandler
2 -->handler: HeartbeatHandler
3    -->handler: AllChannelHandler
4          -->url: providerUrl
5          -->executor: FixedExecutor
6          -->handler: DecodeHandler
7             -->handler: HeaderExchangeHandler
8                -->handler: ExchangeHandlerAdapter(DubboProtocol.requestHandler)
复制代码

其中HeartbeatHandler源码如下:

复制代码
 1 public class HeartbeatHandler extends AbstractChannelHandlerDelegate {
 2 
 3     private static final Logger logger = LoggerFactory.getLogger(HeartbeatHandler.class);
 4 
 5     public static String KEY_READ_TIMESTAMP = "READ_TIMESTAMP";
 6 
 7     public static String KEY_WRITE_TIMESTAMP = "WRITE_TIMESTAMP";
 8 
 9     public HeartbeatHandler(ChannelHandler handler) {
10         super(handler);
11     }
12 
13     public void connected(Channel channel) throws RemotingException {
14         setReadTimestamp(channel);
15         setWriteTimestamp(channel);
16         handler.connected(channel);
17     }
18 
19     public void disconnected(Channel channel) throws RemotingException {
20         clearReadTimestamp(channel);
21         clearWriteTimestamp(channel);
22         handler.disconnected(channel);
23     }
24 
25     public void sent(Channel channel, Object message) throws RemotingException {
26         setWriteTimestamp(channel);
27         handler.sent(channel, message);
28     }
29 
30     public void received(Channel channel, Object message) throws RemotingException {
31         setReadTimestamp(channel);
32         if (isHeartbeatRequest(message)) {
33             Request req = (Request) message;
34             if (req.isTwoWay()) {
35                 Response res = new Response(req.getId(), req.getVersion());
36                 res.setEvent(Response.HEARTBEAT_EVENT);
37                 channel.send(res);
38                 if (logger.isInfoEnabled()) {
39                     int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
40                     if (logger.isDebugEnabled()) {
41                         logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
42                                 + ", cause: The channel has no data-transmission exceeds a heartbeat period"
43                                 + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
44                     }
45                 }
46             }
47             return;
48         }
49         if (isHeartbeatResponse(message)) {
50             if (logger.isDebugEnabled()) {
51                 logger.debug(
52                         new StringBuilder(32)
53                                 .append("Receive heartbeat response in thread ")
54                                 .append(Thread.currentThread().getName())
55                                 .toString());
56             }
57             return;
58         }
59         handler.received(channel, message);
60     }
61 
62     private void setReadTimestamp(Channel channel) {
63         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
64     }
65 
66     private void setWriteTimestamp(Channel channel) {
67         channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
68     }
69 
70     private void clearReadTimestamp(Channel channel) {
71         channel.removeAttribute(KEY_READ_TIMESTAMP);
72     }
73 
74     private void clearWriteTimestamp(Channel channel) {
75         channel.removeAttribute(KEY_WRITE_TIMESTAMP);
76     }
77 
78     private boolean isHeartbeatRequest(Object message) {
79         return message instanceof Request && ((Request) message).isHeartbeat();
80     }
81 
82     private boolean isHeartbeatResponse(Object message) {
83         return message instanceof Response && ((Response) message).isHeartbeat();
84     }
85 }
复制代码
  • 连接完成时:设置lastRead和lastWrite
  • 连接断开时:清空lastRead和lastWrite
  • 发送消息时:设置lastWrite
  • 接收消息时:设置lastRead

之后交由AllChannelHandler进行处理。之后会一直交由HeaderExchangeHandler进行处理。其对lastRead和lastWrite也做了设置和清理:

复制代码
 1     public void connected(Channel channel) throws RemotingException {
 2         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
 3         channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
 4         ...
 5     }
 6 
 7     public void disconnected(Channel channel) throws RemotingException {
 8         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
 9         channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
10         ...
11     }
12 
13     public void sent(Channel channel, Object message) throws RemotingException {
14         Throwable exception = null;
15         try {
16             channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
17             ...
18         } catch (Throwable t) {
19             exception = t;
20         }
21     }
22 
23     public void received(Channel channel, Object message) throws RemotingException {
24         channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
25         ...
26     }
复制代码
  • 连接完成时:设置lastRead和lastWrite
  • 连接断开时:也设置lastRead和lastWrite(为什么?)
  • 发送消息时:设置lastWrite
  • 接收消息时:设置lastRead

 这里里有个疑问,从handler链来看,无论是请求还是响应都会按照handler链来处理一遍。那么在HeartbeatHandler中已经进行了lastWrite和lastRead的设置,为什么还要在HeaderExchangeHandler中再处理一遍?

最后,provider端认为连接断了,则会关闭channel。来看一下NettyChannel的close方法:

复制代码
 1     public void close() {
 2         // 1 将close属性设为true
 3         try {
 4             super.close();
 5         } catch (Exception e) {
 6             logger.warn(e.getMessage(), e);
 7         }
 8         // 2 从全局NettyChannel缓存器中将当前的NettyChannel删掉
 9         try {
10             removeChannelIfDisconnected(channel);
11         } catch (Exception e) {
12             logger.warn(e.getMessage(), e);
13         }
14         // 3 清空当前的NettyChannel中的attributes属性
15         try {
16             attributes.clear();
17         } catch (Exception e) {
18             logger.warn(e.getMessage(), e);
19         }
20         // 4 关闭netty的channel,执行netty的channel的优雅关闭
21         try {
22             if (logger.isInfoEnabled()) {
23                 logger.info("Close netty channel " + channel);
24             }
25             channel.close();
26         } catch (Exception e) {
27             logger.warn(e.getMessage(), e);
28         }
29     }
复制代码

从上边代码来看,假设consumer端挂了,provider端的心跳检测机制可以进行相关的资源回收,所以provider端的心跳检测机制是有必要的。

 

二、consumer端心跳机制

复制代码
                      //创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接
                      -->getClients(URL url)
                        -->getSharedClient(URL url)
                          -->ExchangeClient exchangeClient = initClient(url)
                            -->Exchangers.connect(url, requestHandler)
                              -->HeaderExchanger.connect(URL url, ExchangeHandler handler)
                                -->new DecodeHandler(new HeaderExchangeHandler(handler)))
                                  -->Transporters.connect(URL url, ChannelHandler... handlers)
                                    -->NettyTransporter.connect(URL url, ChannelHandler listener)
                                      -->new NettyClient(url, listener)
                                        -->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
                                        -->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
                                        -->doOpen()//开启netty客户端
                                        -->doConnect()//连接服务端,建立长连接
                                -->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true
                                  -->startHeatbeatTimer()//启动心跳计数器
复制代码

客户端在initClient(url)中设置了heartbeat参数(默认为60s,用户自己设置的方式见“一”中所讲),如下:

复制代码
 1     /**
 2      * Create new connection
 3      */
 4     private ExchangeClient initClient(URL url) {
 5         ...
 6         // enable heartbeat by default
 7         url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
 8 
 9         ...
10 
11         ExchangeClient client;
12         try {
13             // connection should be lazy
14             if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
15                 client = new LazyConnectExchangeClient(url, requestHandler);
16             } else {
17                 client = Exchangers.connect(url, requestHandler);
18             }
19         } catch (RemotingException e) {
20             throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
21         }
22         return client;
23     }
复制代码

与provider类似,来看一下最后开启心跳检测的地方。

复制代码
 1 public class HeaderExchangeClient implements ExchangeClient {
 2     private static final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("dubbo-remoting-client-heartbeat", true));
 3     private final Client client;
 4     private final ExchangeChannel channel;
 5     // heartbeat timer
 6     private ScheduledFuture<?> heartbeatTimer;
 7     // heartbeat(ms), default value is 0 , won't execute a heartbeat.
 8     private int heartbeat;
 9     private int heartbeatTimeout;
10 
11     public HeaderExchangeClient(Client client, boolean needHeartbeat) {
12         if (client == null) {
13             throw new IllegalArgumentException("client == null");
14         }
15         this.client = client;
16         this.channel = new HeaderExchangeChannel(client);
17         String dubbo = client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);
18         this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != null && dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0);
19         this.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
20         if (heartbeatTimeout < heartbeat * 2) {
21             throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
22         }
23         if (needHeartbeat) {
24             startHeatbeatTimer();
25         }
26     }
27 
28     private void startHeatbeatTimer() {
29         stopHeartbeatTimer();
30         if (heartbeat > 0) {
31             heartbeatTimer = scheduled.scheduleWithFixedDelay(
32                     new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
33                         public Collection<Channel> getChannels() {
34                             return Collections.<Channel>singletonList(HeaderExchangeClient.this);
35                         }
36                     }, heartbeat, heartbeatTimeout),
37                     heartbeat, heartbeat, TimeUnit.MILLISECONDS);
38         }
39     }
40 
41     private void stopHeartbeatTimer() {
42         if (heartbeatTimer != null && !heartbeatTimer.isCancelled()) {
43             try {
44                 heartbeatTimer.cancel(true);
45                 scheduled.purge();
46             } catch (Throwable e) {
47                 if (logger.isWarnEnabled()) {
48                     logger.warn(e.getMessage(), e);
49                 }
50             }
51         }
52         heartbeatTimer = null;
53     }
54 }
复制代码

主要看一下startHeartbeatTimer()方法,与provider相同,只是provider是获取NettyServer的所有的NettyChannel,而consumer只是获取当前的对象。

consumer的handler处理链与provider完全相同。

最后来看一下consumer的重连机制:AbstractClient#reconnect

复制代码
 1     public void reconnect() throws RemotingException {
 2         disconnect();
 3         connect();
 4     }
 5 
 6     public void disconnect() {
 7         connectLock.lock();
 8         try {
 9             destroyConnectStatusCheckCommand();
10             try {
11                 Channel channel = getChannel();
12                 if (channel != null) {
13                     channel.close();
14                 }
15             } catch (Throwable e) {
16                 logger.warn(e.getMessage(), e);
17             }
18             try {
19                 doDisConnect();
20             } catch (Throwable e) {
21                 logger.warn(e.getMessage(), e);
22             }
23         } finally {
24             connectLock.unlock();
25         }
26     }
27 
28     protected void connect() throws RemotingException {
29         connectLock.lock();
30         try {
31             if (isConnected()) {
32                 return;
33             }
34             initConnectStatusCheckCommand();
35             doConnect();
36             if (!isConnected()) {
37                 throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
38                         + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
39                         + ", cause: Connect wait timeout: " + getTimeout() + "ms.");
40             } else {
41                 if (logger.isInfoEnabled()) {
42                     logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
43                             + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
44                             + ", channel is " + this.getChannel());
45                 }
46             }
47             reconnect_count.set(0);
48             reconnect_error_log_flag.set(false);
49         } catch (RemotingException e) {
50             throw e;
51         } catch (Throwable e) {
52             throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
53                     + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
54                     + ", cause: " + e.getMessage(), e);
55         } finally {
56             connectLock.unlock();
57         }
58     }
复制代码

代码比较简单,先断连,再连接。

 

posted @ 2022-05-21 15:36  hanease  阅读(54)  评论(0编辑  收藏  举报