引入服务的入口ReferenceBean。
ReferenceBean.java
除了多实现了FactoryBean接口其他的结构与ServiceBean(参考https://blog.csdn.net/qq_23536449/article/details/102639873)几乎差不多。我们知道FactoryBean是一种特殊的bean是;它是一个能生产对象的工厂Bean,它的实现和工厂模式及修饰器模式很像,在Dubbo的实现中FactoryBean接口的作用:创建代理Bean
afterPropertiesSet()
@Override
@SuppressWarnings({"unchecked"})
public void afterPropertiesSet() throws Exception {
//ReferenceBean没有配置consumer,从applicationContext加载ConsumerConfig
//获取到了进行配置好了
if (getConsumer() == null) {
Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, false, false);
if (consumerConfigMap != null && consumerConfigMap.size() > 0) {
ConsumerConfig consumerConfig = null;
for (ConsumerConfig config : consumerConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (consumerConfig != null) {
throw new IllegalStateException("Duplicate consumer configs: " + consumerConfig + " and " + config);
}
consumerConfig = config;
}
}
if (consumerConfig != null) {
setConsumer(consumerConfig);
}
}
}
//ApplicationConfig属性赋值
if (getApplication() == null
&& (getConsumer() == null || getConsumer().getApplication() == null)) {
Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
ApplicationConfig applicationConfig = null;
for (ApplicationConfig config : applicationConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (applicationConfig != null) {
throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
}
applicationConfig = config;
}
}
if (applicationConfig != null) {
setApplication(applicationConfig);
}
}
}
//ModuleConfig属性应用赋值
if (getModule() == null
&& (getConsumer() == null || getConsumer().getModule() == null)) {
Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
ModuleConfig moduleConfig = null;
for (ModuleConfig config : moduleConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (moduleConfig != null) {
throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
setModule(moduleConfig);
}
}
}
//注册中心RegistryConfig应用并赋值
if ((getRegistries() == null || getRegistries().isEmpty())
&& (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().isEmpty())
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().isEmpty())) {
Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
if (registryConfigMap != null && registryConfigMap.size() > 0) {
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
for (RegistryConfig config : registryConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
registryConfigs.add(config);
}
}
if (registryConfigs != null && !registryConfigs.isEmpty()) {
super.setRegistries(registryConfigs);
}
}
}
//监控中心MonitorConfig应用并赋值
if (getMonitor() == null
&& (getConsumer() == null || getConsumer().getMonitor() == null)
&& (getApplication() == null || getApplication().getMonitor() == null)) {
Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
MonitorConfig monitorConfig = null;
for (MonitorConfig config : monitorConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (monitorConfig != null) {
throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
setMonitor(monitorConfig);
}
}
}
//饥饿加载判断
Boolean b = isInit();
if (b == null && getConsumer() != null) {
b = getConsumer().isInit();
}
if (b != null && b.booleanValue()) {
getObject();
}
}
该方法通过检查有没有给类似<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.study.day01.xml.service.EchoService"/>配置了ConsumerConfig、ApplicationConfig、ModuleConfig、RegistryConfig(多个)、MonitorConfig;没有的话通过在Spring的上下文容器中查找这些配置,并设置到ReferenceBean的属性里。最后根据init属性判断是否应该饥饿创建代理对象!
getObject()
@Override
public Object getObject() throws Exception {
return get();
}
FactoryBean接口提供的方法;该方法用于创建代理对象。就是说我们的ReferenceBean最终创建的bean就是getObject()方法创建的bean
ReferenceConfig.get()
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
//检测ref是否为null,为null调用init创建
if (ref == null) {
init();
}
return ref;
}
init()
private void init() {
//已经初始化的 不需要再次初始化
if (initialized) {
return;
}
initialized = true;
//检测接口名称是否为null
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
}
//检查ConsumerConfig是否配置了没有配置,创建,并根据系统环境变量配置consumer
checkDefault();
appendProperties(this);
//判断是否泛化调用
if (getGeneric() == null && getConsumer() != null) {
setGeneric(getConsumer().getGeneric());
}
//判断generic字符串是否不为空并且(generic属性为true或者nativejava或者bean)
//设置interfaceClass为GenericService
if (ProtocolUtils.isGeneric(getGeneric())) {
interfaceClass = GenericService.class;
} else {
try {
//加载该interfaceClass类,没有找到抛出异常
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
//接口interfaceClass与 methodConfig的配置是否存在或者合理即,有些
//method是否在interfaceClass中
checkInterfaceAndMethods(interfaceClass, methods);
}
//加载interfaceName变量对应的系统变量
String resolve = System.getProperty(interfaceName);
String resolveFile = null;
//如果系统中没有interfaceName的配置
if (resolve == null || resolve.length() == 0) {
//加载系统变量中的dubbo.resolve.file属性
resolveFile = System.getProperty("dubbo.resolve.file");
//如果系统变量没有dubbo.resolve.file
if (resolveFile == null || resolveFile.length() == 0) {
//加载用户系统根目录下的dubbo-resolve.properties文件
File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
//如果存在获取 resolveFile的绝对路径
if (userResolveFile.exists()) {
resolveFile = userResolveFile.getAbsolutePath();
}
}
//如果存在resolveFile读取文件中的 interfaceName属性
if (resolveFile != null && resolveFile.length() > 0) {
Properties properties = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(resolveFile));
properties.load(fis);
} catch (IOException e) {
throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
} finally {
try {
if (null != fis) fis.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
resolve = properties.getProperty(interfaceName);
}
}
//如果resolve不为null
if (resolve != null && resolve.length() > 0) {
// 将 resolve 赋值给 url
url = resolve;
if (logger.isWarnEnabled()) {
if (resolveFile != null) {
logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
} else {
logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
}
}
}
/**
* registries和monitor的配置优先级为<dubbo:consumer的优先于<dubbo:module优先于<dubbo:application
*/
if (consumer != null) {
if (application == null) {
application = consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries = consumer.getRegistries();
}
if (monitor == null) {
monitor = consumer.getMonitor();
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
//检查并配置ApplicationConfg
checkApplication();
//检测本地存根
checkStub(interfaceClass);
checkMock(interfaceClass);
Map<String, String> map = new HashMap<String, String>();
Map<Object, Object> attributes = new HashMap<Object, Object>();
//添加side、dubbo、timestamp、pid到map中
map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
//不是泛化服务调用,在map中放入revision、methods、interface信息
if (!isGeneric()) {
//获取版本
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
}
//获取interfaceClass的方法放入map
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put("methods", Constants.ANY_VALUE);
} else {
map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
map.put(Constants.INTERFACE_KEY, interfaceName);
//将ApplicationConfig、ModuleConfig、ConsumerConfig、ReferenceConfig的属性值放入到map中
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, consumer, Constants.DEFAULT_KEY);
appendParameters(map, this);
//prefix--->group/interfaceName:version
//比如com.xxx.xxxService 或者dubbo/com.xxx.xxxService:1.2.1
String prefix = StringUtils.getServiceKey(map);
//如果methodConfig存在
if (methods != null && !methods.isEmpty()) {
//遍历MethodConfig
for (MethodConfig method : methods) {
//将method的属性添加methodName前缀后放到map中谢谢
appendParameters(map, method, method.getName());
//map中是否有methodName.retry;删除掉并用methodName.retries代替这种配置
String retryKey = method.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
//将
if ("false".equals(retryValue)) {
添加重试次数配置 methodName.retries = 0
map.put(method.getName() + ".retries", "0");
}
}
// 添加 MethodConfig 中的“属性”字段放入 attributes
// 比如 onreturn、onthrow、oninvoke 等
appendAttributes(attributes, method, prefix + "." + method.getName());
//attributes的key com.gysoft.xxx.XxxService.hello.onreturn.method value 方法对象
checkAndConvertImplicitConfig(method, map, attributes);
}
}
//服务消费者的ip地址
String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
if (hostToRegistry == null || hostToRegistry.length() == 0) {
//获取本机地址
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
//map中放入register.ip属性 值为消费者的ip
map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
//将attributes放入 system context对象中
StaticContext.getSystemContext().putAll(attributes);
//创建代理
ref = createProxy(map);
/**
* 创建一个消费者模型对象
* 主要为了保存消费方的名称、消费方的配置元数据、ref、接口方法
* 然后放入到ApplicationModel的一个map中
*/
ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
}
- a.初始化状态判断
- b.ConsumerConfig是否配置判断,没配置通过系统变量(System.getProperty()或者指定目录下的配置文件)初始化
- c.ReferenceConfig通过系统变量(System.getProperty()或者指定目录下的配置文件)初始化,感觉这是为了兼容1.0的版本
- d.是否泛化调用判断处理,从系统属性或配置文件中加载与接口名相对应的配置,并将解析结果赋值给 url 字段。url 字段的作用一般是用于点对点调用。
- e.ConsumerConfig、MonitorConfig、RegistryConfig配置根据优先级初始化
- f.检查核心配置类是否为空,为空则尝试从其他配置类中获取
- g.收集消费者配置到map中,还包括对MethodConfig 实例的处理。该实例包含了事件通知配置,比如 onreturn、onthrow、oninvoke 等
- i.消费者ip的获取
- j.封装一个提供者模型对象ConsumerModel对象,并加入缓存,各位同学可以通过ApplicationModel的相关get方法获得ConsumerModel,感兴趣的可以研究下吧。
createProxy(Map<String, String> map)
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
//本地jvm引用配置
if (isInjvm() == null) {
// url 配置被指定,则不做本地引用
if (url != null && url.length() > 0) {
isJvmRefer = false;
// 根据 url 的协议、scope 以及 injvm 等参数检测是否需要本地引用
// 比如如果用户显式配置了 scope=local,此时 isInjvmRefer 返回 true
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
//injvm配置
isJvmRefer = isInjvm().booleanValue();
}
//本地jvm引用
if (isJvmRefer) {
//new一个本地引用url injvm协议的并将Map的参数添加到URL对象中
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
//创建invoker实例
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
//url不为空,指定的URL可能是对点对直连地址,也可能是注册中心URL
if (url != null && url.length() > 0) {
//按照;切分url字符串(因为url可能配置多个!!!)
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
//将字符串转换为URL对象
URL url = URL.valueOf(u);
//设置接口全限定名称的url
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
//检测 url 协议是否为 registry,若是,表明用户想使用指定的注册中心
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
//将map变成?key=value&key2=value2这种的,然后作为refer属性的值添加到url中
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
//合并 url,移除服务提供者的一些配置(这些配置来源于用户配置的url属性)
//比如线程池相关配置。并保留服务提供者的部分配置,比如版本,group,时间戳等
//后将合并后的配置设置为 url 查询字符串中。
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else {
//加载注册中心url
//registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&dubbo=2.0.2&
// pid=16304®istry=zookeeper×tamp=1573441590938
List<URL> us = loadRegistries(false);
if (us != null && !us.isEmpty()) {
for (URL u : us) {
//加载监控中心配置,赞不关心
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
//添加refer参数到url中
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls.isEmpty()) {
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) {
//调用RegistryProtocol的refer方法创建invoker
invoker = refprotocol.refer(interfaceClass, urls.get(0));
//多个注册中心或多个服务提供者,或者两中情况都存在
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
//获取所有Invoker
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // use last registry url
}
}
//如果注册中心的url是没问题的;使用AvaliableCluster
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));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
//如果要检查提供者是否存在但是invoker不可用,抛出异常
if (c && !invoker.isAvailable()) {
// make it possible for consumer to retry later if provider is temporarily unavailable
initialized = false;
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
// create service proxy
// 生成代理类
return (T) proxyFactory.getProxy(invoker);
}
- 1.是否为本地jvm引用判断,如果本地jvm引用则最终会通过InJvmProtocol.refer方法创建InJvmInvoker
- 2.在暴露invoker前对url的处理:首先是直接配置了服务引用的url的处理,然后是通过注册中心对服务引用的处理
- 3.对服务引用的urls大小进行判断,如果等于1通过 Protocol 自适应拓展类构建 Invoker 实例接口,若 urls 元素数量大于1,即存在多个注册中心或服务直连 url,此时先根据 urls构建 Invoker列表,最后使用cluster合并多个invoker成为1个。
- 4.服务引用的可用性判断
- 5.使用proxyFactory创建代理对象
在上篇我们分析了ReferenceConfig.createProxy(),该方法通过调用refprotocol.refer(interfaceClass,url)创建Invoker实例;根据dubbo的spi自适应扩展机制我们不难推测出refprotocol类型为Protocol$Adaptive
Protocol$Adaptive.refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1)
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
经过dubbo的spi自适应扩展机制后Protocol被包装为ProtocolListenerWrapper如下,然后按如下顺序执行protocol的refer方法
ProtocolListenerWrapper.refer(Class<T> type, URL url)
QosProtocolWrapper.refer(Class<T> type, URL url)
该方法开启了qos服务器
ProtocolFilterWrapper..refer(Class<T> type, URL url)
RegistryProtocol.refer(Class<T> type, URL url)
@Override
@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
//获取url中的registry属性的值没有则为dubbo,然后设置协议为registry对应的值,处理后的url类似如下这个样子
//对于通过点对点直连方式的url会变成dubbo://xxxx这种形式
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&dubbo=2.0.2&pid=16684&
// refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
// interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=16684&
// register.ip=169.254.22.149&side=consumer×tamp=1573450786523®istry=zookeeper×tamp=1573450792295
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
//获取到的为ZookeeperRegistry
Registry registry = registryFactory.getRegistry(url);
// 如果是RegistryService
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// url中的查询字符串转换成map
// refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
// interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=16684&
// register.ip=169.254.22.149&side=consumer×tamp=1573450786523®istry=zookeeper×tamp=1573450792295
// 变成了Map qs
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
// 获取group如果为null
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
//如果存在多个group或者group为* group="a,b" or group="*"
//则使用MergeableCluster
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
//只有一个组或者没有组配置,根据dubbo的spi机制默认情况下为FailoverCluster,执行doRefer
return doRefer(cluster, registry, type, url);
}
首先是对于url协议的处理,毕竟有些url是通过直连(不经过注册中心)方式调用,然后是获取注册中心ZookeeperRegistry(当然也有可能为DubboRegistry由于工作中很少用,这里不做分析),然后根据是否为多group选择不同的cluster进行doCluster方法的调用,关于cluster的用处放在后续的博客进行分析
doRefer(Cluster cluster, Registry registry, Class<T> type, URL url)
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//创建RegistryDirectory
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// 将所有refer属性放入到map
// refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
// interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=16684&
// register.ip=169.254.22.149&side=consumer×tamp=1573450786523®istry=zookeeper×tamp=1573450792295
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
//consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&check=false
// &dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService
// &methods=echo,addListener&pid=16244&side=consumer×tamp=1573451795393
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)) {
//给subscrbeUrl添加category属性和check属性
//consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&
//category=consumers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
//interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=23400&
// side=consumer×tamp=1573451977816
URL registeredConsumerUrl = getRegisteredConsumerUrl(subscribeUrl, url);
registry.register(registeredConsumerUrl);
//设置directory的registered属ConsumerUrl属性
directory.setRegisteredConsumerUrl(registeredConsumerUrl);
}
//subscribeUrl添加category属性provder,configurators,routers
//并调用RegistryDirectory的subscribe方法
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;
}
该方法创建了RegistryDirectory对象,,然后生成服务者消费者URL对象,并向注册中心进行注册该节点,注册完毕后,调用RegistryDirectory对象的subscribe方法订阅providers、configurators、routers节点下的数据。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker,最后通过调用ProviderConsumerRegTable.registerConsumer方法将与该消费者有关的数据封装为ConsumerInvokerWrapper对象,并放入到内存中
public static void registerConsumer(Invoker invoker, URL registryUrl, URL consumerUrl, RegistryDirectory registryDirectory) {
ConsumerInvokerWrapper wrapperInvoker = new ConsumerInvokerWrapper(invoker, registryUrl, consumerUrl, registryDirectory);
String serviceUniqueName = consumerUrl.getServiceKey();
Set<ConsumerInvokerWrapper> invokers = consumerInvokers.get(serviceUniqueName);
if (invokers == null) {
consumerInvokers.putIfAbsent(serviceUniqueName, new ConcurrentHashSet<ConsumerInvokerWrapper>());
invokers = consumerInvokers.get(serviceUniqueName);
}
invokers.add(wrapperInvoker);
}
RegistryDirectory.java
- Directory:目录接口抽象,提供根据路由规则返回Invoker列表的能力
public interface Directory<T> extends Node {
/**
* get service type.
* 服务的类型
*
* @return service type.
*/
Class<T> getInterface();
/**
* list invokers.
* invokers列表
*
* @return invokers
*/
List<Invoker<T>> list(Invocation invocation) throws RpcException;
}
- AbstractDirectory:目录的抽象实现:从此目录的列表方法返回的调用者列表已由路由器过滤
/**
* 注册中心url
* zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&dubbo=2.0.2&pid=49676&
* refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
* interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=49676&
* register.ip=169.254.22.149&side=consumer×tamp=1573561006651×tamp=1573561007009
*/
private final URL url;
private volatile boolean destroyed = false;
/**
* 消费方的url对象
* consumer://xxxx这种
*/
private volatile URL consumerUrl;
/**
* dubbo路由机制实现,通过订阅routes节点route://xxx
* 解析而成
*/
private volatile List<Router> routers;
- NotifyListener:实现该接口的类能够接收到zookeeper的节点数据变动通知
- RegistryDirectory:注册中心的目录实现,服务目录中存储了一些和服务提供者有关的信息,通过服务目录,服务消费者可获取到服务提供者的信息,比如 ip、端口、服务协议等。通过这些信息,服务消费者就可通过 Netty 等客户端进行远程调用。在一个服务集群中,服务提供者数量并不是一成不变的,如果集群中新增了一台机器,相应地在服务目录中就要新增一条服务提供者记录。或者,如果服务提供者的配置修改了,服务目录中的记录也要做相应的更新。
/**
* 集群策略,默认为failover。
*/
private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
/**
* 路由工厂,可以通过监控中心或治理中心配置。
*/
private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
/**
* 配置实现工厂类
*/
private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();
/**
* 服务key,默认为服务接口名。com.alibaba.dubbo.registry.RegistryService,注册中心在Dubbo中也是使用服务暴露。
*/
private final String serviceKey; // Initialization at construction time, assertion not null
/**
* 服务提供者接口类,例如interface com.alibaba.dubbo.demo.DemoService
*/
private final Class<T> serviceType; // Initialization at construction time, assertion not null
/**
* 服务消费者URL中的所有属性。
*/
private final Map<String, String> queryMap; // Initialization at construction time, assertion not null
/**
* 注册中心URL,只保留消息消费者URL查询属性,也就是queryMap。
*/
private final URL directoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
/**
* 引用服务提供者方法数组。
*/
private final String[] serviceMethods;
/**
* 多个服务组
*/
private final boolean multiGroup;
/**
* 协议
*/
private Protocol protocol; // Initialization at the time of injection, the assertion is not null
/**
* 注册中心
*/
private Registry registry; // Initialization at the time of injection, the assertion is not null
private volatile boolean forbidden = false;
private volatile URL overrideDirectoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
private volatile URL registeredConsumerUrl;
/**
* override rules
* Priority: override>-D>consumer>provider
* Rule one: for a certain provider <ip:port,timeout=100>
* Rule two: for all providers <* ,timeout=5000>
*/
/**
* 配置信息。
*/
private volatile List<Configurator> configurators; // The initial value is null and the midway may be assigned to null, please use the local variable reference
// Map<url, Invoker> cache service url to invoker mapping.
/**
* 服务URL对应的Invoker(服务提供者调用器)。
*/
private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
/**
* methodInvokerMap; methodName : List< Invoker< T >>,
* dubbo:method 对应的Invoker缓存表。
*/
// Map<methodName, Invoker> cache service method to invokers mapping.
private volatile Map<String, List<Invoker<T>>> methodInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
/**
* 缓存invokerUrls到invokers的映射
*/
// Set<invokerUrls> cache invokeUrls to invokers mapping.
private volatile Set<URL> cachedInvokerUrls; // The initial value is null and the midway may be assigned to null, please use the local variable reference
public RegistryDirectory(Class<T> serviceType, URL url) {
super(url);
//参数校验
if (serviceType == null)
throw new IllegalArgumentException("service type is null.");
if (url.getServiceKey() == null || url.getServiceKey().length() == 0)
throw new IllegalArgumentException("registry serviceKey is null.");
this.serviceType = serviceType;
//获取注册中心URL的serviceKey:com.alibaba.dubbo.registry.RegistryService。
this.serviceKey = url.getServiceKey();
//获取zookeeper://xxx中的refer属性的值,应该像如下这样子
//application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
//interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&
// pid=24572®ister.ip=169.254.22.149&side=consumer×tamp=1573453015233
this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
//overrideDirectoryUrl和directoryUrl属性赋值;移除注册中心URL的监控中心以及其他属性值,只保留消息消费者的配置属性。
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&check=false&
//dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&
//methods=echo,addListener&pid=24924®ister.ip=169.254.22.149&side=consumer×tamp=1573453320189
this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
//获取directoryUrl中的group属性
String group = directoryUrl.getParameter(Constants.GROUP_KEY, "");
//是否为多组配置
this.multiGroup = group != null && ("*".equals(group) || group.contains(","));
//获取methods属性,都是方法名称
String methods = queryMap.get(Constants.METHODS_KEY);
//数组
this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);
}
RegistryDirectory的构造方法并没有做很特别的事情呢,都是给自身的成员变量进行初始化:比如serviceType、serviceKey、queryMap、overrideDirectoryUrl、directoryUrl、multiGroup、serviceMethods。
getRegisteredConsumerUrl(subscribeUrl, url)
public URL getRegisteredConsumerUrl(final URL consumerUrl, URL registryUrl) {
return consumerUrl.addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY,
CHECK_KEY, String.valueOf(false));
}
给subscribeUrl添加category和check属性,添加后的registeredConsumerUrl类似:consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer& category=consumers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3& interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=23400& side=consumer×tamp=1573451977816
ZookeeperRegistry.register(registeredConsumerUrl)
该方法我们在服务暴露已经分析过了,执行后,创建节点consumer,如下图
未完待续~