创建代理和连接server详细分析
由于整个过程超级复杂,整个分析的思路是先整体再局部,将复杂的流程拆分为多个部分逐个分析,这样比较容易理解。
整理连接server流程图如下
如果直连方式直接调用 DubboProtocol,如果配置了注册中心则通过注册中心获取 server url列表。然后调用DubboProtocol。(DubboProtocol代表与服务端建立连接创建执行代理invoker, 其中包含了创建过滤链的流程。)
线上 我们常用的是 订阅创建代理方式 与服务端通信,所以我们的目标是分析阅创建代理过程。
由于订阅创建代理完整的时序特别长,总结出下图
如上图可以分为3个节点
- 获取执行代理inovker,创建动态代理.
- 向注册中心订阅服务端信息
- 与服务端建立连接创建执行代理invoker
对应下图的 1,2,3
这个图是简略时序图,图中只给出一些关键的步骤。
订阅创建代理分析计划
先分析创建动态代理,然后分析直连创建代理,最后分析订阅创建代理
这样做的原因 先分析创建动态代理 是对整个流程的切入点有了解,知道有那些内容需要分析。
订阅创建代理流程很复杂。包含了直连创建代理的流程,先分析这部分,有助于后续的分析。
创建动态代理分析
代理类方法 createProxy 分析
loc:ReferenceConfig<T>.createProxy(Map<String,String>)
createProxy:创建代理类,详细处理如下
判断是否是InJVM,如果是就创建InJVM类型 invoker, 否则如果是直连方式,解析直连配置字符串,获取连接列表,走直接方式创建Invoker,否则如果是注册中心方式,获取多个注册中心配置列表 走订阅流程创建Invoker,然后使用javasist生成代理类型。
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
//...
//直连的方式也可以配置注册中心,根据是否配置注册中心做不同的处理
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));//添加必要属性
} else {
// url=dubbo://10.103.11.210:20880/tuling.dubbo.server.UserService
// map toString:{dubbo=2.5.7, timestamp=1551177583993, methods=getUser, register.ip=10.103.11.210, application=young-app, side=consumer, pid=9052, interface=tuling.dubbo.server.UserService, loadbalance=roundrobin}
//url在 直连时 是set进来的 。合并map到url中去
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else {
// 配置了注册中心,将注册中心的配置 附加基本配置,组装成注册中心配置列表(有可能配置了多个注册中心)
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
//添加监控中心地址
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size() == 0) {//抛出异常
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
if (urls.size() == 1) {
//创建执行代理invoker,配置注册中心则返回 MockClusterInovoker 否则返回ProtocolFilterWrapper$1 invoker
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个registry url
}
}
//直连的方式配置了注册中心
if (registryURL != null) { // 有 注册中心协议的URL
// 对有注册中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);//指定容错层,AvailableCluster在执行时会检查invoker是否可用
//StaticDirectory 静态目录 相对于RegistryDirectory 而言,里面的invoker列表是不可改变的。
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {//获取配置是否检查
c = consumer.isCheck();
}
if (c == null) {//默认检查
c = true; // default true
}// 如果检查 但是invoker不可用。抛出异常
if (c && !invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}