上一章介绍了Dubbo的服务与注册中心交互图spring
交互过程以下:app
上一章介绍了第一个操做,即服务的发布;这一章将介绍第2、三两个操做,简单的说,就是服务的引用。负载均衡
消费者引用dubbo服务,能够经过spring的xml配置jvm
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
dubbo:reference将转换成ReferenceBean,最终造成对远程提供者服务的代理,即对服务的引用。ReferenceBean继承ReferenceConfig抽象配置类,同时实现多个Spring相关的容器接口。ide
其中最重要的是FactoryBean接口,它是spring的一个重要的扩展方式。对于dubbo:reference建立的spring Bean,并非想要ReferenceBean这个对象自己,而是想获取对远程服务的代理。经过FactoryBean.getObject()来建立基于服务接口的代理,从而将复杂的实现封装,使用户能够不关注底层的实现。url
getObject方法调用ReferenceConfig的get方法,继而调用init方法初始化服务引用。init方法对消费者的配置进行校验和组装,和服务发布同样,造成建立URL的map对象,而后调用createProxy方法建立代理。.net
if (isJvmRefer) { // 组装localhost本地URL,经过InjvmProtocol进行服务引用 URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map); invoker = refprotocol.refer(interfaceClass, url); if (logger.isInfoEnabled()) { logger.info("Using injvm service " + interfaceClass.getName()); } }
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=1528&qos.port=22222®istry=zookeeper×tamp=1530743640901
URL monitorUrl = loadMonitor(u); if (monitorUrl != null) { map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString())); }
u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))
组装完的registryUrl以下:代理
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=4772&qos.port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D4772%26qos.port%3D33333%26register.ip%3D192.168.199.180%26side%3Dconsumer%26timestamp%3D1531358326151®istry=zookeeper×tamp=1531358326266
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>(); URL registryURL = null; // 遍历registryUrl,建立对远程服务的引用Invoker执行器 for (URL url : urls) { invokers.add(refprotocol.refer(interfaceClass, url)); if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { registryURL = url; // use last registry url } } // 将多个Invoker经过集群策略假装成一个Invoker if (registryURL != null) { // registry url is available // use AvailableCluster only when register's cluster is available URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); invoker = cluster.join(new StaticDirectory(u, invokers)); } else { // not a registry url invoker = cluster.join(new StaticDirectory(invokers)); }
return (T) proxyFactory.getProxy(invoker);
上面第5步,将registryUrl经过protocol.refer获取服务引用,根据SPI机制,会先通过ProtocolListenerWrapper, ProtocolFilterWrapper,但这两个Wrapper都过滤了注册中心Url,直接调用Registry.refer方法。code
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { // 设置url的protocol为registry key的值 url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY); // 根据url的protocol从注册中心工厂返回对应的Registry // 如zookeeper即返回ZookeeperRegistry Registry registry = registryFactory.getRegistry(url); // 若是服务引用interface就是RegistryService,则直接返回Invoker if (RegistryService.class.equals(type)) { return proxyFactory.getInvoker((T) registry, type, url); } // 若是存在多个group分组,使用MergeableCluster // 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); }
注册和订阅都在doRefer中完成router
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 // RegistryDirectory获取url的parameters为refer_key对应的url中的parameters Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters()); // 建立URL,根据不一样的category_key分别做为consumerUrl(注册)和subscribeUrl(订阅) 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中增长category=consumers&check=false registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY, Constants.CHECK_KEY, String.valueOf(false))); } // 向注册中心订阅,订阅目录为category=providers,configurators,routers,回调RegistryDirectory的notify方法 directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + Constants.CONFIGURATORS_CATEGORY + "," + Constants.ROUTERS_CATEGORY)); // 经过集群策略将目录服务统一暴露成一个Invoker执行器,默认集群策略为failover Invoker invoker = cluster.join(directory); // 本地注册表注册消费者 ProviderConsumerRegTable.registerConsuemr(invoker, url, subscribeUrl, directory); return invoker; }
执行步骤以下:
这里介绍下提供者和消费者服务在zookeeper上对应节点path:
官方提供的zookeeper节点图:
zookeeper经过watcher机制实现对节点的监听,节点数据变化经过节点上的watcher回调客户端。dubbo的消费者订阅了提供者URL的目录节点,若是提供者节点数据变化,会主动回调NotifyListener(RegistryDirectory实现了NotifyListener)的notify方法,将节点下全部提供者的url返回,从而从新生成对提供者服务的引用的可执行invokers,供目录服务持有。
RegistryDirectory.notify(urls)方法过滤category=providers的提供者url(invokerUrl),而后调用refreshInvoker(invokerUrls)方法,进行基础校验后,调用toInvokers(invokerUrls)方法,经过protocol.refer方法转换成Invoker。
RegistryDirectory notify(urls) -> refreshInvoker(invokerUrls) -> toInvokers(invokerUrls) -> invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
这里的protocol根据providerUrl的protocol基于SPI机制获取,通常就是DubboProtocol。于是最终造成对提供者服务的引用是经过DubboProtocol.refer方法。
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { optimizeSerialization(url); // create rpc invoker. // getClients方法返回ExchangeClient对象 DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers); invokers.add(invoker); return invoker; }
参考:https://blog.csdn.net/quhongwei_zhanqiu/article/details/41651487