• 第四章 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     }
    复制代码

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

  • 相关阅读:
    《Cracking the Coding Interview》——第6章:智力题——题目2
    《Cracking the Coding Interview》——第6章:智力题——题目1
    《Cracking the Coding Interview》——第5章:位操作——题目8
    《Cracking the Coding Interview》——第5章:位操作——题目7
    《Cracking the Coding Interview》——第5章:位操作——题目6
    Spyder 调出绘图界面
    作为非计算机专业的学生,觉得 C 语言远比其他语言易于上手,正常吗?
    vs2015 + Python3.5 环境搭建
    更新32位Spyder从3.0.0-> 3.2.3
    luogu P1047 校门外的树 x
  • 原文地址:https://www.cnblogs.com/hanease/p/16295217.html
Copyright © 2020-2023  润新知