注:本文基于dubbo2.6.x
1.com.alibaba.dubbo.config.ReferenceConfig#createProxy
在《深度解析dubbo服务本地引用(injvm)》一文中,我们分析了dubbo的本地引用,本文接着ReferenceConfig#createProxy 方法下下半部分接着分析。
if (isJvmRefer) {// jvm
...
} else {
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
....
} else { // assemble URL from register center's configuration
List<URL> us = loadRegistries(false);
if (us != null && !us.isEmpty()) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {// 如果监控url存在的话,就将 monitor 塞到map中
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}/// 将url添加到urls, refer= k=v&k1=v1&k2=v2
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls.isEmpty()) {// urls
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 = 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; // use last registry url
}
}
if (registryURL != null) { // registry url is available
// use AvailableCluster only when register's cluster is available
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, );
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // not a registry url
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
这里我们就找着重要的说,分为两个大部分,首先是获取url,将获取的url添加到urls,接着就是判断urls的size,如果是一个的话
直接调用 refprotocol.refer(interfaceClass, urls) 获得invoker ,如果是多个的话就会循环调用然后塞到invokers,之后将多个invoker 进行join合并成一个。
下面我们就看下 refprotocol.refer(interfaceClass, urls) 这个是怎样生成invoker的。
这里这个refprotocol是 private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
是Protocol接口自适应类,这里我们protocol是registry,所以这个refprotocol.refer其实最后就调到了RegistryProtocol。
下面我们看下com.alibaba.dubbo.registry.integration.RegistryProtocol#refer方法。
2.com.alibaba.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
return doRefer(cluster, registry, type, url);
}
第一行是将url 中parameters 里面的registry的值获取出来,如果你是用zk作为注册中心,获取出来的就是zookeeper,然后设置到url的protocol属性上,之后就是将parameters 的 registry remove掉了,这时候url的protocol 就是zookeeper了
第二行 就是根据 url的protocol来获取RegistryFactory 的实现类,也就是ZookeeperRegistryFactory ,然后就是调用它的getRegistry()方法,获取Registry ,总而言之就是获取 注册中心的Registry 对象。
接着就是如果你这个接口类型是RegistryService 的话,就找proxyFactory 来生成invoker。
再往下走就是解析将url中refer 属性值取出来,然后解析成map,其实这个refer就是在ReferenceConfig#createProxy 中塞进去的,就是一些属性,然后获取group 属性,进行分组。走到最后就是调用了doRefer()方法,我么看下这个doRefer方法。
3.com.alibaba.dubbo.registry.integration.RegistryProtocol#doRefer
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//创建Directory 对象
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
URL registeredConsumerUrl = getRegisteredConsumerUrl(subscribeUrl, url);
registry.register(registeredConsumerUrl);// 注册
directory.setRegisteredConsumerUrl(registeredConsumerUrl);
}
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
//创建invoker对象
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
首先是创建了个RegistryDirectory 对象,这个对象非常重要,Directory 是目录的意思,而这里就是多个invoker,这里维护了invoker列表,而且这个列表是会根据注册中心变动的。
接着就是将registry与protocol设置进去,再往下就是生成一个subscribeUrl。如果subscribeUrl 的ServiceInterface不是* ,register属性是true,这时候生成一个registeredConsumerUrl,进行注册,然后将registeredConsumerUrl 设置到directory 的consumerUrl属性上。
接着就是调用 directory.subscribe 的方法进行订阅,之后就是 cluster.join(directory); 获取invoker ,这个cluster 是dubbo spi注入进来的,
我们看下这个Cluster的定义:
@SPI() //默认是failover 失败重试
public interface Cluster {
/**
* Merge the directory invokers to a virtual invoker.
* 基于Directory 对象创建invoker对象
* @param <T>
* @param directory
* @return cluster invoker
* @throws RpcException
*/
@Adaptive //基于 Dubbo SPI Adaptive 机制,加载对应的 Cluster 实现,使用 URL.cluster 属性
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
这里用户没有特殊配置的话,就是使用FailoverCluster 子类的join方法。但是使用dubbo spi的时候,还有包装机制,会将FailoverCluster对象外层包上一个MockClusterWrapper,所以我们需要先看下MockClusterWrapper
public class MockClusterWrapper implements Cluster {
// 这个cluster才是真正的 FailoverCluster
private Cluster cluster;
public MockClusterWrapper(Cluster cluster) {
this.cluster = cluster;
}
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new MockClusterInvoker<T>(directory,
this.cluster.join(directory));
}
}
我们看到先是调用了cluster.join()方法,然后将返回的invoker对象用MockClusterInvoker 包了一层,这里这个cluster 才是真正的FailoverCluster对象,我们来看看它干了啥。
public class FailoverCluster implements Cluster {
public final static String NAME = "failover";
// 实现接口join方法 ,创建 对应invoker处理
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new FailoverClusterInvoker<T>(directory); /// new 一个失败重试的invoker
}
}
我们可以看到new FailoverClusterInvoker 对象返回了,这个FailoverClusterInvoker 其实是一个失败重试invoker。
我们一会在来看FailoverClusterInvoker 内部实现机制,因为我们现在已经得到了invoker对象,现在我们一层一层返回去,这时候我们发现退回到了最初com.alibaba.dubbo.config.ReferenceConfig#createProxy方法。
上面这个红框框起来的是我们刚才调用的,这是urls就一个是情况,多个的url的时候我们看到是循环来获得invoker,然后取得最后一个invoker赋值给registryURL,最后将多个invoker合成了一个。接着再往下走:
接着就是是否需要检查invoker的可用性,当check=false的时候不会检查,默认是true的,如果没有可以使用的provider就会抛出异常,这个错误应该常见的。
接着就是创建 proxy代理类了,我们在《深度解析dubbo服务本地引用(injvm)》中讲过这部分内容,我们再来回顾下这个proxy生成是什么样子的。
4. Proxy代理类生成过程
(T) proxyFactory.getProxy(invoker)
我们先来看看这个ProxyFactory这个接口,三个抽象方法,这个getInvoker方法我们在服务暴露那几篇已经探讨过了,根据dubbo spi 的自适应规则,先去找url中PROXY_KEY 的值,如果有值的话使用我们自定义的,如果没有值的话就使用@SPI注解里面实现类JavassistProxyFactory。
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
// 是否范化调用
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
JavassistProxyFactory 继承父类AbstractProxyFactory ,然后这个两个getProxy方法都是在父类实现的
这个一个参数的getProxy调用两个参数的getProxy,然后范化参数是false,表示不范化调用。
我们可以看到先从invoker的url里面获取interfaces,这个一看就就是多个interface的,我们这里是null,直接走了是null的情况,将我们自己业务接口class 与EchoService class 放到了interfaces中,下面那个是范化调用的封装,我们暂时先不管它,接着就是调用子类的getProxy(invoker,Class<?>[] types) 方法。
我们可以直接看下生成的proxy代码。
这里我把上篇文章里的截图拿过来了。
我们看到在方法调用的时候 其实是调用的invoker中的invoke方法,由这个invoker来帮你处理,这篇主要是讲了Proxy代理创建的大体流程,然后最后得到我们在业务调用方法的时候其实是调用的invoker的invoke方法,我们在下篇主要是讲下这个invoker 里面的invoke方法是怎样子执行的。