第四章 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×tamp=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×tamp=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 }
代码比较简单,先断连,再连接。