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 }

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

第四章 dubbo源码解析目录的更多相关文章

  1. 第零章 dubbo源码解析目录

    第一章 第一个dubbo项目 第二章  dubbo内核之spi源码解析 2.1  jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...

  2. 第十四章 Executors源码解析

    前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...

  3. 第四章 CopyOnWriteArraySet源码解析

    注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...

  4. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  5. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  6. dubbo源码解析-spi(4)

    前言 本篇是spi的第四篇,本篇讲解的是spi中增加的AOP,还是和上一篇一样,我们先从大家熟悉的spring引出AOP. AOP是老生常谈的话题了,思想都不会是一蹴而就的.比如架构设计从All in ...

  7. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

  8. dubbo源码解析-spi(一)

    前言 虽然标题是dubbo源码解析,但是本篇并不会出现dubbo的源码,本篇和之前的dubbo源码解析-简单原理.与spring融合一样,为dubbo源码解析专题的知识预热篇. 插播面试题 你是否了解 ...

  9. 第六章 ReentrantLock源码解析2--释放锁unlock()

    最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...

  10. 第九章 LinkedBlockingQueue源码解析

    1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...

随机推荐

  1. Typecho框架个人博客搭建方法学习

    使用Typecho框架一个月又十二天了,就目前感觉来说,整体还不错,很多方面都支持个性化,二次开发,但是目前MD编辑器有一丢丢问题,不能同步滚动条滚动,就是编辑器区域滚动,预览区域没有动静,需要两边都 ...

  2. 常见return错误

    常见return错误 3221225477 (0xC0000005): 访问越界,一般是读或写了野指针指向的内存. 3221225725 (0xC00000FD): 堆栈溢出,一般是无穷递归造成的. ...

  3. MobaXterm连接Ensp回车显示^M,无法敲回车并且报错

    最近,在使用MobaXterm连接ensp的时候,发现输入回车键,却不能出现回车的效果,反而打出了^M字符. 临时解决办法: 永久解决办法: 加入以下三行,可以永久关闭回显 [MottyOptions ...

  4. 基于surging的木舟平台如何构建起微服务

    一.概述 木舟平台分为微服务平台和物联网平台, 上面几篇都是介绍如何通过网络组件接入设备,那么此篇文章就细致介绍下在木舟平台下如何构建微服务. 木舟 (Kayak) 是什么? 木舟(Kayak)是基于 ...

  5. WSL(Ubuntu)连接 Windows 的 USB 设备(完结)

    前言 最近使用 Linux 通过串口与设备通信,之前使用 Linux 都是在 VMware 里创建虚拟机,该平台下若有串口通信需求,有专门的按键功能切换很方便. 但切换了 WSL2 (windows ...

  6. 设置 crossdomain.xml 文件实施 HTTP 流式传输

    本文概括介绍了跨域策略文件,以及如何在 Adobe Media Server 中为 HTTP 流式传输配置该文件. 为什么需要采用 crossdomain.xml 文件? 跨域策略文件 跨域策略文件是 ...

  7. AspNetCore全局异常处理

    在开发ASP.NET Core应用程序时,全局异常处理是一个重要的概念.它允许我们集中处理应用程序中未捕获的异常,确保应用程序的稳定性和用户体验. 1. 为什么需要全局异常处理 全局异常处理的目的是为 ...

  8. 前端跨平台调试代理神器Whistle

    概述 抓包一直是平时开发中经常要做的,有一款好用的抓包工具可以让开发体验更棒,以前一直用fiddler,fiddler虽然强悍,但是入手有一定难度,而且较笨重,今天介绍另一款抓包工具:Whistle. ...

  9. vtkDelaunay2D 错误 Edge not recovered, polygon fill suspect

    vtkDelaunay2D 在设定SetSourceData边界处理凹多边形时,不稳定,有概率会出现"Edge not recovered, polygon fill suspect&quo ...

  10. 【位运算】codeforces 1775 C. Interesting Sequence

    题意 输入一个正整数 \(T(1 \leq T \leq 2000)\),代表 \(T\) 组测试用例.对于每个测试用例: 输入两个整数 \(n, m(0 \leq n, m \leq 10^{18} ...