在消费者通过refer()方法获得Invoker的时候同时完成对于相关方法的订阅。以默认的dubbo作为注册中心为例子。
当消费者调用refer()方法的时候由于当中的Url中的protocol为registry,所调用的refer()方法实则为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);
}
首先,尝试获得registry服务,通过registryFactory的getRegistry()方法。这里采用默认的dubbo实现。但是getRegistry()方法实现在AbstractRegistryFactory中。
@Override
public Registry getRegistry(URL url) {
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
String key = url.toServiceString();
// Lock the registry access process to ensure a single instance of the registry
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);
return registry;
} finally {
// Release the lock
LOCK.unlock();
}
}
这里直接把url所要注册的服务改为RegistryService,作为注册中心也需要实现的接口。在这个方法里,会根据生成的注册中心服务url找寻注册中心服务的实例,如果已经生成过,直接返回,否则,通过createRegistry()方法生成新的注册中心。
这里默认的dubbo实现的registryFatctory为DubboRegistryFactory,createRegistryFactory()方法也实现在这里。
public Registry createRegistry(URL url) {
url = getRegistryURL(url);
List<URL> urls = new ArrayList<URL>();
urls.add(url.removeParameter(Constants.BACKUP_KEY));
String backup = url.getParameter(Constants.BACKUP_KEY);
if (backup != null && backup.length() > 0) {
String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(backup);
for (String address : addresses) {
urls.add(url.setAddress(address));
}
}
RegistryDirectory<RegistryService> directory = new RegistryDirectory<RegistryService>(RegistryService.class, url.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()).addParameterAndEncoded(Constants.REFER_KEY, url.toParameterString()));
Invoker<RegistryService> registryInvoker = cluster.join(directory);
RegistryService registryService = proxyFactory.getProxy(registryInvoker);
DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);
directory.setRegistry(registry);
directory.setProtocol(protocol);
directory.notify(urls);
directory.subscribe(new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, RegistryService.class.getName(), url.getParameters()));
return registry;
}
这里构造了registryService的代理,在这里,注册中心被当做一个dubbo应用,与其连接实则为registryService服务的远程调用,不同的是,注册中心被作为生产者,采用的是地址直连的方式与当前消费者完成连接。这里subscribe()方法的调用实则调用的是DubboRegistry中的RegistryServce的方法,这个RegistryServce也是与一般方法一样经过代理最后由InvokerInvoctionHandler所调用的,其实就是一次普通的方法发远程调用。这里的url被加入了回调参数,尤其是subscribe()方法的第二个参数也就是listener的回调属性被设置为true,也就是说注册中心对于这个参数的调用会回调消费者。
在获得了registry之后,就可以回到registryProtocpol中,继续通过doRefer()进行所需要接口方法的订阅。
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
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)) {
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
这里的registry就是刚才生成的registryService,通过远程调用的方式以接口作为参数,来完成接口方法的订阅。在默认的dubbo协议下,异步等待注册中心的回复。而相应的invoker也通过join()方法得到,默认得到的是FailoverClusterInvoker。
注册中心需要在subscribe()方法中的末尾远程回调lintener的notify()方法,达到服务地址的回调,保证消费者所订阅的服务消息能够被消费者所得知,并调用。