上篇我们介绍了Consumer消费端调用流程解析,地址如下Dubbo源码解读-Consumer调用流程解析-CSDN博客
本文主要针Dubbo消费端Exchange初始化过程,从dubbo源码角度进行解析。
大家可以好好仔细读一下本文。有疑问欢迎留言。
接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。
不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。
如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。
主要内容
- ExchangeClient初始化背景知识
- ExchangeClient执行链
- ExchangeClient初始化源码解析
- 图解ExchangeClient调用链。
ExchangeClient初始化背景知识
ExchangeClient初始化时机
消费端服务列表刷新时,RegistryDirectroy类实现NotifyListener接口,监听注册中心节点。当消费者启动或者服务列表发生变动时候,监听事件会包装providers节点写的服务,创建DubboInvoker,刷新本地服务列表。创建DubboInvoker的时候会创建ExchangeClient。更细节的流程可跳转 Dubbo源码解读-Consumer消费端服务列表刷新-CSDN博客。
getClients(url)创建的ExchangeClient对象。
# DubboProtocol.java
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// create rpc invoker.
//getClients(url)是核心逻辑
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
同样服务端在服务暴露时候,创建DubboExport,启动NettyServer时候创建ExchangeServer。更细节的流程可跳转至 Dubbo源码解析-Provider服务暴露Export源码解析-CSDN博客
ExchangeClient的作用
- Consumer和Provider之间通讯的桥梁。消费端通过NettyClient,发送请求和接收服务端的响应。服务端通过NettyServer接收消费端的请求和发送返回结果。
- 持有Handler的执行链。消费端接收到响应或者服务端接收到请求之后,按照Handler的执行链处理数据。
ExchangeClient的执行链
- 消费端链路:
- 请求链路:HeaderExchangeClient.request()->HeaderExchangeChannel.request()->NettyClient.send()->nettyChannel
- 响应链路:NettyClientHandler->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->defalutFutrue.doReceive()
- 服务端链路:
- 请求链路:HeaderExchangeServer->*->NettyServer->NettyServerHandler
- 响应链路:NettyServerHandler->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->ExchangeHandlerAdapter.reply()->DubboExporter.getInvoker->Invoker执行[Filter1-》Filter2-〉invokerDelegete->AbstractProxyInvoker->wrapper->ServiceClass]
ExchangeClient初始化源码解析
Exchange属于信息交换层,用于消费者发送请求,提供者接受请求,数据编码解码等,以request/response为中心。
概要流程
- DubboProtocol.refer创建DubboInvoker。构造函数执行getClient.
- 创建HeaderExchangeClient对象,持有NettyClient。NettyClient持有Handler执行链。
- 创建DubboInvoker对象,持有client对象,通过client对象发送请求和接受服务端响应。
详细流程
- consummer端的notify订阅事件(服务列表刷新)
- 最后会调用到DubboProtocol.Refer(),初始化client
- DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
- getClients(url)
- 获取连接个数配置:connections
- 一个ExchangeClient,就对应一个TCP长链接。
- ExchangeClient[] clients = new ExchangeClient[connections];
- 创建长连接initClient(url);服务端列表中,每一个服务端对应一个长链接。
- url设置编解码codec
- url设置心跳时间。heartbeat
- 返回HeaderExchangeClient,TCP长链接,由这个client发起调用。这里建立了一个很长的链式调用的对象关系。
- 持有NettyClient,Netty客户端
- 构造函数:构造Handler调用
- doOpen():开启Netty客户端
- connect():连接到netty服务端
- 连接成功后的channel赋值一下
- 开启心跳线程
- 持有NettyClient,Netty客户端
源码解析
1.DubboProtocol。根据配置的连接数,创建连接。
private ExchangeClient[] getClients(URL url) {
// whether to share connection
boolean service_share_connect = false;
//获取connections配置参数,配置客户端跟服务端建立几个长连接
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
// if not configured, connection is shared, otherwise, one connection for one service
if (connections == 0) {
service_share_connect = true;
connections = 1;
}
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
//如没有配置,就是建立一个长连接
if (service_share_connect) {
clients[i] = getSharedClient(url);
} else {
clients[i] = initClient(url);
}
}
return clients;
}
2.DubboProtocol.getSharedClient。
private ExchangeClient getSharedClient(URL url) {
String key = url.getAddress();
ReferenceCountExchangeClient client = referenceClientMap.get(key);
if (client != null) {
if (!client.isClosed()) {
client.incrementAndGetCount();
return client;
} else {
referenceClientMap.remove(key);
}
}
locks.putIfAbsent(key, new Object());
synchronized (locks.get(key)) {
if (referenceClientMap.containsKey(key)) {
return referenceClientMap.get(key);
}
//初始化长连接
ExchangeClient exchangeClient = initClient(url);
client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
//建立缓存
referenceClientMap.put(key, client);
ghostClientMap.remove(key);
locks.remove(key);
return client;
}
}
3.DubboProtocol.initClient。创建Client并构造handler的执行链。
private ExchangeClient initClient(URL url) {
// client type setting.
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
// enable heartbeat by default
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// BIO is not allowed since it has severe performance issue.
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported client type: " + str + "," +
" supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
}
ExchangeClient client;
try {
// connection should be lazy
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
client = new LazyConnectExchangeClient(url, requestHandler);
} else {
//如果非懒加载 requestHandler是最后调用的handler
client = Exchangers.connect(url, requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
}
return client;
}
4. 最后会调到HeaderExchanger。构建HeaderExchangeClient对象。持有NettyClient对象和Handler的执行链。
@Override
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
//这里建立了一个很长的链式调用的对象关系,是责任链的变种
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}
5.HeaderExchangeClient
public HeaderExchangeClient(Client client, boolean needHeartbeat) {
if (client == null) {
throw new IllegalArgumentException("client == null");
}
this.client = client;
this.channel = new HeaderExchangeChannel(client);
String dubbo = client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);
this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != null && dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0);
this.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
if (heartbeatTimeout < heartbeat * 2) {
throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
}
//开启心跳线程
if (needHeartbeat) {
startHeartbeatTimer();
}
}
图解ExchangeClient的执行链
消费端调用服务方法,会通过MockClusterInvoker->FailOverClusterInvoker->DubboInvoker.invoke->Client发送请求。具体怎么调用到DubboInvoker的,感兴趣的同学可以跳转到 Dubbo源码解读-Consumer调用流程解析-CSDN博客
下图为client和handler发送和接受服务端响应的流程。其中client部分为发送请求流程,handler负责处理服务端响应的结果。
总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。
知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。
当然如果觉得文章写的不错,也可点击大赏按钮,深深的鼓励一下博主。哈哈!!!!