第四章 dubbo源码解析目录
9.1 客户端发起请求源码
来看一下客户端请求代码:
1 DemoService demoService = (DemoService) context.getBean("demoService"); // 获取远程服务代理
2 String hello = demoService.sayHello("world"); // 执行远程方法
在8.2 构建客户端源码解析中我们看到最终得到的demoService是一个proxy0代理对象。现在来分析第二行代码。
一 客户端请求总体流程

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

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

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

这里的handler就是InvokerInvocationHandler

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

首先将请求参数封装成一个RpcInvocation实例,如下:
-->String methodName=sayHello
-->Class<?>[] parameterTypes=[class java.lang.String]
-->Object[] arguments=[world]
-->Map<String, String> attachments={}
之后使用MockClusterInvoker.invoke(Invocation invocation)进行远程调用:

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

注意:这里可以做服务降级,后续会说。
之后调用FailoverClusterInvoker.invoke方法,该方法在其父类AbstractClusterInvoker中,

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

首先是获取一个List<Invoker<T>>,之后获取一个LoadBalance,最后调用doInvoke进行调用。
首先来看通过RegistryDirectory.list(Invocation invocation),该方法在RegistryDirectory的父类AbstractDirectory中:

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

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

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

其中Map<String, List<Invoker<T>>> methodInvokerMap在8.2 构建客户端源码解析已经初始化好了:
Map<String, List<Invoker<T>>> methodInvokerMap={
sayHello=[provider1的RegistryDirectory$InvokerDelegete实例, provider2的RegistryDirectory$InvokerDelegete实例], *=[provider1的RegistryDirectory$InvokerDelegete实例, provider2的RegistryDirectory$InvokerDelegete实例]}
这里根据方法名sayHello取出两个RegistryDirectory$InvokerDelegete实例。最后通过Router进行过滤,这里只有一个Router,就是MockInvokersSelector。

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

这里直接返回了。到此就已经选出可以被调用的RegistryDirectory$InvokerDelegete实例子集了。记下来先获取负载均衡器,默认是RandomLoadBalance。最后执行FailoverClusterInvoker.
doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance):

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

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

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

RandomLoadBalance.doSelect

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

最后来看RegistryDirectory$InvokerDelegete.invoke,该方法实际在其父类InvokerWrapper中:
1 private final Invoker<T> invoker;//ListenerInvokerWrapper
2
3 public Result invoke(Invocation invocation) throws RpcException {
4 return invoker.invoke(invocation);
5 }
ListenerInvokerWrapper.invoke
1 private final Invoker<T> invoker;//ProtocolFilterWrapper$Invoker
2
3 public Result invoke(Invocation invocation) throws RpcException {
4 return invoker.invoke(invocation);
5 }
之后就会执行一系列的filter,这些filter后续会讲,现在直接执行到DubboInvoker.invoke,实际上该方法在其父类AbstractInvoker中,AbstractInvoker又调用了DubboInvoker.doInvoke:

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

其中ExchangeClient[] clients在8.2 构建客户端源码解析已经被初始化好了:
1 ExchangeClient[] clients = [ReferenceCountExchangeClient实例]//如果设置了多条连接,此处有多个client
ReferenceCountExchangeClient.request
1 private ExchangeClient client;//HeaderExchangeClient
2
3 public ResponseFuture request(Object request, int timeout) throws RemotingException {
4 return client.request(request, timeout);
5 }
HeaderExchangeClient.request
1 private final ExchangeChannel channel;//HeaderExchangeChannel
2
3 public ResponseFuture request(Object request, int timeout) throws RemotingException {
4 return channel.request(request, timeout);
5 }
HeaderExchangeChannel.request

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

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

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

NettyChannel.send

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

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

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

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

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

NettyHandler.messageReceived

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

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

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

HeartbeatHandler.received(Channel channel, Object message)

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

AllChannelHandler.received(Channel channel, Object message)

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

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

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

DecodeHandler.received(Channel channel, Object message)

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

HeaderExchangeHandler.received(Channel channel, Object message)

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

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

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

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

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

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

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

Map<String, Exporter<?>> exporterMap在服务暴露时就已经初始化好了。"com.alibaba.dubbo.demo.DemoService:20880"->DubboExporter实例。该实例包含一个呗filter链包裹的Invoker实例:RegistryProtocol$InvokerDelegete实例。
之后开始执行filter链了,直到最后执行到RegistryProtocol$InvokerDelegete.invoke,该方法实际上是在RegistryProtocol$InvokerDelegete的父类InvokerWrapper执行,InvokerWrapper调用AbstractProxyInvoker.invoke(Invocation invocation)。

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

这里先调用子类JavassistProxyFactory$AbstractProxyInvoker.doInvoke,之后将返回结果封装为RpcResult返回。
1 protected Object doInvoke(T proxy, String methodName,
2 Class<?>[] parameterTypes,
3 Object[] arguments) throws Throwable {
4 return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
5 }
这里调用了Wrapper类的invokeMethod方法,Wrapper是一个动态生成的类,笔者给出:

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

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

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

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

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

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

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

DefaultFuture.received(Channel channel, Response response)

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

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

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

netty是一个异步非阻塞的框架,所以当执行channel.send(req);的时候,当其内部执行到netty发送消息时,不会等待结果,直接返回。为了实现“异步转为同步”,使用了DefaultFuture这个辅助类,
在HeaderExchangeChannel.request(Object request, int timeout),在还没有等到客户端的响应回来的时候,就直接将future返回了。返回给谁?再来看HeaderExchangeChannel.request(Object request, int timeout)的调用者。
1 -->DubboInvoker.doInvoke(final Invocation invocation)
2 //获取ExchangeClient进行消息的发送
3 -->ReferenceCountExchangeClient.request(Object request, int timeout)
4 -->HeaderExchangeClient.request(Object request, int timeout)
5 -->HeaderExchangeChannel.request(Object request, int timeout)
DubboInvoker.doInvoke(final Invocation invocation)

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

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

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

此处我们看到当响应response没有回来时,condition会执行await进行阻塞当前线程,直到被唤醒或被中断或阻塞时间到时了。当客户端接收到服务端的响应的时候,DefaultFuture.doReceived:
会先为response赋上返回值,之后执行condition的signal唤醒被阻塞的线程,get()方法就会释放锁,执行returnFromResponse(),返回值。

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

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

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

看一下Request的构造器:

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

看一下DefaultFuture的构造器:

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

再来看一下响应。
HeaderExchangeHandler.handleRequest(ExchangeChannel channel, Request req)

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

来看一下Response的构造器:
1 private long mId = 0;
2
3 public Response(long id, String version) {
4 mId = id;
5 mVersion = version;
6 }
这里response的id的值时request的id。最后来看一下服务端接收后的处理:
DefaultFuture.received(Channel channel, Response response)

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

9.1 客户端发起请求源码、9.2 服务端接收请求消息并发送响应消息源码、9.3 客户端接收响应信息(异步转同步的实现) 分析了dubbo同步调用的源码,现在来看一下dubbo异步调用。
一、使用方式
服务提供方不变,调用方代码如下:
1 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
2 <dubbo:method name="sayHello" async="true" timeout="60000"/>
3 <dubbo:method name="sayBye" async="true" timeout="60000"/>
4 </dubbo:reference>
配置里添加<dubbo:method name="xxx" async="true"/>,表示单个方法xxx使用异步方式;如果demoService下的所有方法都使用异步,直接配置为<dubbo:reference async="true"/>。

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

Consumer启动主类。其中asyncFuture2()方法是推荐用法,注意Callable(asyncCall方法的入参)只是一个任务task,不会新建线程;所以asyncFuture2()和asyncFuture1()相似,资源占用相同,都是用一根线程进行异步操作的。
二、asyncFuture1()源码解析
先来看asyncFuture1(),总体步骤:
- demoService.sayHello("zhangsan"); 创建一个Future对象,存入当前线程的上下文中
- Future<String> helloFuture = RpcContext.getContext().getFuture(); 从当前线程的上下文中获取第一步存入的Future对象
- final String helloStr = helloFuture.get(); 阻塞等待,从Future中获取结果
代码主要执行流(代码详细执行流看文章开头的三篇博客):
1、demoService.sayHello("zhangsan");
-->FutureFilter.invoke(final Invoker<?> invoker, final Invocation invocation)
-->DubboInvoker.doInvoke(final Invocation invocation)
FutureFilter:

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

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

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

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

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

最后,回头看一下FutureFilter:

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


1 private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
2 Future<?> f = RpcContext.getContext().getFuture();
3 if (f instanceof FutureAdapter) {
4 ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
5 future.setCallback(new ResponseCallback() {
6 public void done(Object rpcResult) {
7 if (rpcResult == null) {
8 logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
9 return;
10 }
11 ///must be rpcResult
12 if (!(rpcResult instanceof Result)) {
13 logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
14 return;
15 }
16 Result result = (Result) rpcResult;
17 if (result.hasException()) {
18 fireThrowCallback(invoker, invocation, result.getException());
19 } else {
20 fireReturnCallback(invoker, invocation, result.getValue());
21 }
22 }
23
24 public void caught(Throwable exception) {
25 fireThrowCallback(invoker, invocation, exception);
26 }
27 });
28 }
29 }

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

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

这里判断响应是否已经返回了,如果返回了,直接执行invokeCallback(callback),否则将传入的ResponseCallback对象赋值给callback对象。
2、Future<String> helloFuture = RpcContext.getContext().getFuture();
RpcContext:

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

从当前线程上下文中获取之前存进去的FutureAdapter对象。
3、final String helloStr = helloFuture.get();
helloFuture是上述的FutureAdapter对象,其get()调用的是内部的DefaultFuture的get(),该方法与同步调用时相同,源码分析见文章开头的三篇文章。

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

get方法的超时设置除了直接在xml中配置之外,还可以在代码中手动执行(优先级高)
1 final String helloStr2 = helloFuture.get(7000, TimeUnit.MILLISECONDS);
三、asyncFuture2()源码解析
下面来看一下asyncFuture2()源码:
1、Future<String> helloFuture = RpcContext.getContext().asyncCall(()-> demoService.sayHello("zhangsan"));

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

这里外层的catch的作用是什么?没搞清楚 https://github.com/alibaba/dubbo/issues/1346
2、final String helloStr = helloFuture.get();
与同步相同。
总结:dubbo异步与同步的差别:
- 同步:创建DefaultFuture之后,直接get阻塞等待;
- 异步:创建DefaultFuture之后,使用FutureAdapter进行包装,之后设置到当前线程的RpcContext中;后续用户在合适的时候自己从RpcContext获取future,之后get。
dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html
一、使用方式
两个服务:
- DemoService:真正要调用的服务
- Notify:事件通知服务(用在consumer端)
provider:
1 package com.alibaba.dubbo.demo;
2
3 public interface DemoService {
4 String sayHello(String name);
5 }

1 public class DemoServiceImpl implements DemoService {
2 @Override
3 public String sayHello(String name) {
4 throw new RpcException("ex, param: " + name);//测试onthrow方法
5 // return "Hello " + name;//测试onreturn方法
6 }
7 }

consumer:
通知服务:Notify

1 package com.alibaba.dubbo.demo.consumer.eventnotify;
2
3 public interface Notify {
4 void oninvoke(String name); // 调用之前
5 void onreturnWithoutParam(String result); // 调用之后
6 void onreturn(String result, String name); // 调用之后
7 void onthrow(Throwable ex, String name); // 出现异常
8 }


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

xml配置:
1 <bean id="notifyService" class="com.alibaba.dubbo.demo.consumer.eventnotify.NotifyService"/>
2 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
3 <dubbo:method name="sayHello" timeout="60000" oninvoke="notifyService.oninvoke" onreturn="notifyService.onreturnWithoutParam" onthrow="notifyService.onthrow"/>
4 </dubbo:reference>
之后就可以运行Consumer启动类,之后调用demoService.sayHello(String name)了。
注意:
- oninvoke方法:
- 必须具有与真实的被调用方法sayHello相同的入参列表:例如,oninvoke(String name)
- onreturn方法:
- 至少要有一个入参且第一个入参必须与sayHello的返回类型相同,接收返回结果:例如,onreturnWithoutParam(String result)
- 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收sayHello入参的:例如, onreturn(String result, String name)
- onthrow方法:
- 至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果;例如,onthrow(Throwable ex)
- 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收sayHello入参的:例如,onthrow(Throwable ex, String name)
- 如果是consumer在调用provider的过程中,出现异常时不会走onthrow方法的,onthrow方法只会在provider返回的RpcResult中含有Exception对象时,才会执行。(dubbo中下层服务的Exception会被放在响应RpcResult的exception对象中传递给上层服务)
二、源码解析
整个事件通知的逻辑都在FutureFilter中,来看一下源码:

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

从@Activate(group = Constants.CONSUMER)来看FutureFilter只用在consumer端;不管是同步调用还是异步调用,都会走FutureFilter。
原理:
- 首先走oninvoke(String name)方法;
- 然后走sayHello(String name)
- 最后根据同步还是异步分别走不同的逻辑。
其中同步很简单,看sayHello(String name)的返回结果RpcResult中是否有exception对象,如果有,执行onthrow(Throwable ex, String name);如果没有执行onreturnWithoutParam(String result)。
异步的操作:由于不知道provider什么时候回执行完毕,所以要添加回调等待provider端返回结果后,再执行onthrow(Throwable ex, String name)或者onreturnWithoutParam(String result),这种模式很重要,这是统计异步方法调用时间的一种非常好的模式。
重点看一下异步!
三、异步回调模式

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

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

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


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

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

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

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

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

上边的代码只是一个形式,实际上start时间需要在调用sayHello方法之前进行记录。
dubbo的心跳机制:
- 目的:检测provider与consumer之间的connection连接是不是还连接着,如果连接断了,需要作出相应的处理。
- 原理:
- provider:dubbo的心跳默认是在heartbeat(默认是60s)内如果没有接收到消息,就会发送心跳消息,如果连着3次(180s)没有收到心跳响应,provider会关闭channel。
- consumer:dubbo的心跳默认是在60s内如果没有接收到消息,就会发送心跳消息,如果连着3次(180s)没有收到心跳响应,consumer会进行重连。
来看源码调用链。先看provider端。
一、provider端心跳机制

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

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

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

其中:int DEFAULT_HEARTBEAT = 60 * 1000,即当用户没有配置heartbeat(心跳时间)时,默认heartbeat=60s(即60s内没有接收到任何请求,就会发送心跳信息)。那么这个heartbeat到底该怎么配?
provider端:
1 <dubbo:service ...>
2 <dubbo:parameter key="heartbeat" value="3000"/>
3 </dubbo:service>
consumer端:
1 <dubbo:reference ...>
2 <dubbo:parameter key="heartbeat" value="3000"/>
3 </dubbo:reference>
再来看调用链,当执行到这一句。
1 ChannelHandler.wrapInternal(ChannelHandler handler, URL url)
会形成一个handler调用链,调用链如下:

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

这也是netty接收到请求后的处理链路,注意其中有一个HeartbeatHandler。
最后,执行new HeaderExchangeServer(Server server),来看源码:

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

创建HeaderExchangeServer时,初始化了heartbeat(心跳间隔时间)和heartbeatTimeout(心跳响应超时时间:即如果最终发送的心跳在这个时间内都没有返回,则做出响应的处理)。
- heartbeat默认是0(从startHeatbeatTimer()方法可以看出只有heartbeat>0的情况下,才会发心跳,这里heartbeat如果从url的parameter map中获取不到,就是0,但是我们在前边看到dubbo会默认设置heartbeat=60s到parameter map中,所以此处的heartbeat=60s);
- heartbeatTimeout:默认是heartbeat*3。(原因:假设一端发出一次heartbeatRequest,另一端在heartbeat内没有返回任何响应-包括正常请求响应和心跳响应,此时不能认为是连接断了,因为有可能还是网络抖动什么的导致了tcp包的重传超时等)
- scheduled是一个含有一个线程的定时线程执行器(其中的线程名字为:"dubbo-remoting-server-heartbeat-thread-*")
之后启动心跳定时任务:
- 首先如果原来有心跳定时任务,关闭原来的定时任务
- 之后启动scheduled中的定时线程,从启动该线程开始,每隔heartbeat执行一次HeartBeatTask任务(第一次执行是在启动线程后heartbeat时)
来看一下HeartBeatTask的源码:

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

HeartBeatTask首先获取所有的channelProvider#getChannels获取所有需要心跳检测的channel,channelProvider实例是HeaderExchangeServer中在启动线程定时执行器的时候创建的内部类。
1 new HeartBeatTask.ChannelProvider() {
2 public Collection<Channel> getChannels() {
3 return Collections.unmodifiableCollection(
4 HeaderExchangeServer.this.getChannels());
5 }
6 }
来看一下HeaderExchangeServer.this.getChannels():

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

实际上就是获取NettyServer中的全部channel连接。
获取到需要心跳检测的channel后,对每一个channel进行如下判断:
- 如果在heartbeat内没有进行读操作或者写操作,则发送心跳请求
- 如果正常消息和心跳在heartbeatTimeout都没接收到,consumer端会进行重连,provider端会关闭channel
这里比较关键的是lastRead和lastWrite的设置。先来看一下获取:
1 Long lastRead = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_READ_TIMESTAMP);
2 Long lastWrite = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);
说明有地方在设置这两个值到channel中。
从请求和响应处理来看,无论是请求还是响应都会按照这个顺序处理一遍。

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

其中HeartbeatHandler源码如下:

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

- 连接完成时:设置lastRead和lastWrite
- 连接断开时:清空lastRead和lastWrite
- 发送消息时:设置lastWrite
- 接收消息时:设置lastRead
之后交由AllChannelHandler进行处理。之后会一直交由HeaderExchangeHandler进行处理。其对lastRead和lastWrite也做了设置和清理:

1 public void connected(Channel channel) throws RemotingException {
2 channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
3 channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
4 ...
5 }
6
7 public void disconnected(Channel channel) throws RemotingException {
8 channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
9 channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
10 ...
11 }
12
13 public void sent(Channel channel, Object message) throws RemotingException {
14 Throwable exception = null;
15 try {
16 channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis());
17 ...
18 } catch (Throwable t) {
19 exception = t;
20 }
21 }
22
23 public void received(Channel channel, Object message) throws RemotingException {
24 channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
25 ...
26 }

- 连接完成时:设置lastRead和lastWrite
- 连接断开时:也设置lastRead和lastWrite(为什么?)
- 发送消息时:设置lastWrite
- 接收消息时:设置lastRead
这里里有个疑问,从handler链来看,无论是请求还是响应都会按照handler链来处理一遍。那么在HeartbeatHandler中已经进行了lastWrite和lastRead的设置,为什么还要在HeaderExchangeHandler中再处理一遍?
最后,provider端认为连接断了,则会关闭channel。来看一下NettyChannel的close方法:

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

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

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

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

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

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

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

主要看一下startHeartbeatTimer()方法,与provider相同,只是provider是获取NettyServer的所有的NettyChannel,而consumer只是获取当前的对象。
consumer的handler处理链与provider完全相同。
最后来看一下consumer的重连机制:AbstractClient#reconnect

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

代码比较简单,先断连,再连接。
第四章 dubbo源码解析目录的更多相关文章
- 第零章 dubbo源码解析目录
第一章 第一个dubbo项目 第二章 dubbo内核之spi源码解析 2.1 jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...
- 第十四章 Executors源码解析
前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...
- 第四章 CopyOnWriteArraySet源码解析
注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...
- Dubbo 源码解析四 —— 负载均衡LoadBalance
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...
- dubbo源码解析五 --- 集群容错架构设计与原理分析
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...
- dubbo源码解析-spi(4)
前言 本篇是spi的第四篇,本篇讲解的是spi中增加的AOP,还是和上一篇一样,我们先从大家熟悉的spring引出AOP. AOP是老生常谈的话题了,思想都不会是一蹴而就的.比如架构设计从All in ...
- dubbo源码解析-spi(3)
前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...
- dubbo源码解析-spi(一)
前言 虽然标题是dubbo源码解析,但是本篇并不会出现dubbo的源码,本篇和之前的dubbo源码解析-简单原理.与spring融合一样,为dubbo源码解析专题的知识预热篇. 插播面试题 你是否了解 ...
- 第六章 ReentrantLock源码解析2--释放锁unlock()
最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...
- 第九章 LinkedBlockingQueue源码解析
1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...
随机推荐
- openresty IP限流
1.针对大流量大并发网络请求下,为了保证服务的正常运行,不得不针对性采取限流的方式来解决大流量带来的服务器的压力. 2.在目前项目中对于接入了不同的平台,所以需要针对具体的平台做相对应的限流,或者针对 ...
- 机器学习中验证两个算法之间是否存在显著差距的t-test检验
同一主题的简单分析版本,建议查看: 机器学习领域中假设检验的使用 本文内容为在上文基础上进一步分析版本. 相关: t检验 t检验应用条件 t检验(t-test) t-test终极指南 一文详解t检验 ...
- 3.14 深度剖析Linux硬链接和软链接,直击它们的本质!
建立硬链接和软链接非常简单,那这两种链接有什么区别?它们都有什么作用?这才是链接文件最不容易理解的地方,我们分别来讲讲. ln创建硬链接 我们再来建立一个硬链接文件,然后看看这两个文件的特点. [ro ...
- Python预安装包制作
预编译安装包 在Linux服务器上,经常会安装Python.Redis.Nginx等服务,不管离线.在线都需要编译.编译之前还需要安装一些依赖的环境,比如,openssl.gcc.g++等,但是mak ...
- fabric基本使用
fabric简介 Fabric 是一个 Python 的库,同时它也是一个命令行工具.它提供了丰富的同 SSH 交互的接口,可以用来在本地或远程机器上自动化.流水化地执行 Shell 命令.使用 ...
- virsh的基本使用
virsh基础命令 1.查看运行的虚拟机 virsh list 查看所有的虚拟机(关闭和运行的,不包括摧毁的) virsh list --all 2..启动虚拟机 virsh start 虚拟机名称 ...
- 内网 BitTorrent 下载环境搭建——基于 Transmission
背景 前段时间为公司的产品增加了磁力链.种子下载的能力,测试时发现网上搜到的热门种子,有时好用,有时不好用,不好用主要表现在:没速度.速度慢.速度不稳定.下载一部分后没速度等,严重拖累了测试工作.为此 ...
- rabbitmq-c与amqp扩展安装
最近需要使用RabbitMQ进行消息队列处理 1.安装rabbitmq-c 在安装amqp之前需要先安装rabbitmq-c扩展 rabbitmq-c下载网址:https://github.com/a ...
- 分布式对象存储之FDFS
1.它是一个开源的分布式文件系统,它对文件进行管理. 功能有:文件存储.文件同步.文件访问(文件的上传下载)等.特别适合以文件为主的在线服务. 2.fastDFS服务端有两个角色:跟踪器(tracke ...
- 新型大语言模型的预训练与后训练范式,苹果的AFM基础语言模型
前言:大型语言模型(LLMs)的发展历程可以说是非常长,从早期的GPT模型一路走到了今天这些复杂的.公开权重的大型语言模型.最初,LLM的训练过程只关注预训练,但后来逐步扩展到了包括预训练和后训练在内 ...