先有个脑图:
直接来到ReferenceBean:
调试的时候如果进不来init,可以这样:
com.alibaba.dubbo.config.ReferenceConfig#init :
检查配置然后将配置构建成 map
com.alibaba.dubbo.config.ReferenceConfig#createProxy:
判断是injvm还是直连还是注册中心模式:
如果是走本地的话,那么直接构建个走本地协议的 URL 然后进行服务的引入,即 refprotocol.refer,这个方法之后会做分析,本地的引入就不深入了,就是去之前服务暴露的 exporterMap 拿到服务
如果不是本地,那肯定是远程了,接下来就是判断是点对点直连 provider 还是通过注册中心拿到 provider 信息再连接 provider 了,我们分析一下配置了 url 的情况,如果配置了 url 那么不是直连的地址,就是注册中心的地址。
然后就是没配置 url 的情况,到这里肯定走的就是注册中心引入远程服务了。
List<URL> us = this.loadRegistries(false);//拿到注册中心
if (us != null && !us.isEmpty()) {
for(Iterator var5 = us.iterator(); var5.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {
u = (URL)var5.next();
url = this.loadMonitor(u);//监控中心
if (url != null) {
map.put("monitor", URL.encode(url.toFullString()));
}
}
}
if (this.urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + this.interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
if (this.urls.size() == 1) {
this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
} else {
....
}
urls:
简述一下就是先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过 URL 上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。
在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。
然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。
com.alibaba.dubbo.registry.integration.RegistryProtocol#refer:
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter("registry", "dubbo")).removeParameter("registry");
Registry registry = this.registryFactory.getRegistry(url);//获取注册中心
if (RegistryService.class.equals(type)) {
return this.proxyFactory.getInvoker(registry, type, url);
} else {
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded("refer"));
String group = (String)qs.get("group");
return group == null || group.length() <= 0 || Constants.COMMA_SPLIT_PATTERN.split(group).length <= 1 && !"*".equals(group) ? this.doRefer(this.cluster, registry, type, url) : this.doRefer(this.getMergeableCluster(), registry, type, url);
}
}
主要就是获取注册中心实例,然后调用 doRefer 进行真正的 refer
com.alibaba.dubbo.registry.integration.RegistryProtocol#doRefer:
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
RegistryDirectory<T> directory = new RegistryDirectory(type, url);
directory.setRegistry(registry);//塞入注册中心实例
directory.setProtocol(this.protocol);//塞入动态生成的protocol
Map<String, String> parameters = new HashMap(directory.getUrl().getParameters());
//生成服务消费者链接
URL subscribeUrl = new URL("consumer", (String)parameters.remove("register.ip"), 0, type.getName(), parameters);
if (!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {
//向注册中心注册服务消费者,在consumers目录下创建新节点
registry.register(subscribeUrl.addParameters(new String[]{"category", "consumers", "check", String.valueOf(false)}));
}
//在订阅注册中心的providers、configurators、routers目录,订阅完就会触发DubboProtocol的refer方法
directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
//封装多个invoker
Invoker invoker = cluster.join(directory);
//记录到注册表
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
这个方法很关键,可以看到生成了RegistryDirectory
这个 directory 并塞入了注册中心实例,它自身也实现了NotifyListener
接口,因此注册中心的监听其实是靠这家伙来处理的。
然后向注册中心注册自身的信息,并且向注册中心订阅了 providers 节点、 configurators 节点 和 routers 节点,订阅了之后 RegistryDirectory 会收到这几个节点下的信息,就会触发 DubboInvoker 的生成了,即用于远程调用的 Invoker。
然后通过 cluster 再包装一下得到 Invoker,因此一个服务可能有多个提供者,最终在 ProviderConsumerRegTable 中记录这些信息,然后返回 Invoker。
所以我们知道Conusmer
是在 RegistryProtocol#refer 中向注册中心注册自己的信息,并且订阅 Provider 和配置的一些相关信息。
拿到了Provider
的信息之后就可以通过监听触发 DubboProtocol# refer 了(具体调用哪个 protocol 还是得看 URL的协议的,我们这里是 dubbo 协议),整个触发流程我就不一一跟一下了,看下调用栈就清楚了
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
this.optimizeSerialization(url);
//拿到接口类,远程服务的url,然后创建一个client
DubboInvoker<T> invoker = new DubboInvoker(serviceType, url, this.getClients(url), this.invokers);
this.invokers.add(invoker);
return invoker;
}
invoker:
调用栈:
这里的重点在 getClients
,因为终究是要跟远程服务进行网络调用的,而 getClients 就是用于获取客户端实例,实例类型为 ExchangeClient,底层依赖 Netty 来进行网络通信,并且可以看到默认是共享连接。
private ExchangeClient[] getClients(URL url) {
boolean service_share_connect = false;
//获取连接数,默认为0
int connections = url.getParameter("connections", 0);
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] = this.getSharedClient(url);//返回共享的客户端
} else {
clients[i] = this.initClient(url);//得到初始化的新的客户端
}
}
return clients;
}
//通过远程地址找 client ,这个 client 还有引用计数的功能,如果该远程地址还没有 client 则调用 initClient
private ExchangeClient getSharedClient(URL url) {
String key = url.getAddress();
ReferenceCountExchangeClient client = (ReferenceCountExchangeClient)this.referenceClientMap.get(key);
if (client != null) {
if (!client.isClosed()) {
client.incrementAndGetCount();
return client;
}
this.referenceClientMap.remove(key);
}
this.locks.putIfAbsent(key, new Object());
synchronized(this.locks.get(key)) {
if (this.referenceClientMap.containsKey(key)) {
return (ExchangeClient)this.referenceClientMap.get(key);
} else {
ExchangeClient exchangeClient = this.initClient(url);
client = new ReferenceCountExchangeClient(exchangeClient, this.ghostClientMap);
this.referenceClientMap.put(key, client);
this.ghostClientMap.remove(key);
this.locks.remove(key);
return client;
}
}
}
private ExchangeClient initClient(URL url) {
//获取客户端类型,默认为netty
String str = url.getParameter("client", url.getParameter("server", "netty"));
url = url.addParameter("codec", "dubbo");
url = url.addParameterIfAbsent("heartbeat", String.valueOf(60000));
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(), " "));
} else {
try {
Object client;
if (url.getParameter("lazy", false)) {
client = new LazyConnectExchangeClient(url, this.requestHandler);
} else {
client = Exchangers.connect(url, this.requestHandler);
}
return (ExchangeClient)client;
} catch (RemotingException var5) {
throw new RpcException("Fail to create remoting client for service(" + url + "): " + var5.getMessage(), var5);
}
}
}
然后最终得到的 Invoker
就是这个样子,就是下面截图的样子:
com.alibaba.dubbo.registry.integration.RegistryProtocol#doRefer返回的
最终将调用 return (T) proxyFactory.getProxy(invoker);
返回一个代理对象,这个就不做分析了。
private T createProxy(Map<String, String> map) {
...
if (this.urls.size() == 1) {
this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList();
...
}
}
..
return proxyFactory.getProxy(this.invoker);
}
}
流程总结: