关注消费者端服务的订阅,我们从ReferenceConfig中的createProxy()开始分析,我们重点先关注其中的一句,当注册中心只有一个(单个或集群)时进入此分支即执行下面这一句。(关于createProxy整个流程会在后面博文中重点介绍)
invoker = refprotocol.refer(interfaceClass, urls.get(0));其中refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();之前关于@SPI的文章中分析过,refProtocol其实是字节码动态生成的适配类。
public class Protocol$Adpative implements Protocol { public Exporter export(Invoker invoker) throws com.alibaba.dubbo.rpc.RpcException { if (invoker == null || invoker.getUrl() == null) { throw new IllegalArgumentException("xxx"); } URL url = invoker.getUrl(); String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol(); if(extName == null) { throw new IllegalStateException("xxx"); } Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName); return extension.export(invoker); } public Invoker refer(Class cls, URL u) throws RpcException { if (u == null) { throw new IllegalArgumentException("url == null"); } URL url = u; String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol(); if(extName == null) { throw new IllegalStateException("xxx"); } Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName); return extension.refer(cls, u); } }
实际调用了其中的refer()方法,我们可以看到SPI的动态选择在这里体现,这里传入的URL的protocol为reistery,所以这里的extension实际上是registryProtocol的实例。这里调用了registryProtocol的refer()方法。
@SuppressWarnings("unchecked") 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数据结构上的protocol改为registry对应的协议(比如multicast或者zookeeper),调用registryFactory的getRegister,根据url返回不同的registry对象,在初始化registry对象时,通过代理和注册中心建立连接。其中registryFactory也是个代理适配器对象(会根据url对应不同的协议实际上可能是ZookeeperRegistryFactory、MulticastRegistryFactory等)。这里采用默认的dubbo实现,其中getRegistry()方法具体逻辑在AbstractRegistryFactory中
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.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 { // 释放锁 LOCK.unlock(); } }
先把要注册的服务跟接口都改为RegistryService(RegistryService是注册中心的接口)。这里会根据生成的注册中心服务url寻找注册中心的实例,如果已经生成过,那么直接返回,否则调用createRegister()生成新的注册中心。
默认是dubbo实现的registryFactory为DubboRegistryFactory,我们来看下具体的createRegistry()方法。
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的代理,(这里,我们可以把注册中心理解成暴露服务端,与注册中心连接的过程可以看成RegistryService的远程方法调用的过程,当然跟其他服务调用不同的是,这里注册中心作为生产者是直接与当前消费者直连,而不是在注册中心得到url,redirect访问生产者)。这里directory的subscribe实际上是调用register的subsubscribe。
public void subscribe(URL url) { setConsumerUrl(url); registry.subscribe(url, this); } protected void doSubscribe(URL url, NotifyListener listener) { registryService.subscribe(url, listener); }实则调用了registryService代理的subscrible,根据之前对代理的分析我们明确知道,他是调用了registryInvoker的invoke方法,其实就是一次普通的远程调用 方法。传入参数的url中被加入了回调参数,listener是变更事件监听器,注册中心通过对这个参数的调用回调给消费者。
在得到registry之后,我们继续registryProtocol的refer()方法。
我们可以看到根据url上面的group的配置,分为group模式跟非group模式,剩下都是调用doRefer(),无非在group模式时返回的invoker为MergeableClusterInvoker,非group模式时返回的invoker为FailoverClusterInvoker。
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); URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters()); 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)); return cluster.join(directory); }
这里的registry就是之前的registryService,通过远程调用的方式,其中url中以接口名字为参数,来完成接口方法的订阅。在默认dubbo协议下异步等待注册中心回调,通过.join()方法得到默认的invoker,FailoverClusterInvoker即远程服务执行体。当然,注册中心需要在subscribe()方法末尾远程回调listener的notify方法,即在完成订阅后告诉消费者,达到服务地址的回调。
小结
通过配置选择合适的协议,生成对应的registryService代理,通过远程回调的方式,连接上注册中心,再通过registryService代理,传递带有接口类型url数据结构,实现在注册中心的服务的订阅,得到服务地址,返回invoker。
说在最后的话
本文可能思路多多少少不是特别清楚,比较我也是边看边写的,等我把生产者服务暴露端的流程过一遍后,再把整个串起来总结一遍。