服务概念
DemoService接口表示一个服务,此时的服务表示服务定义
DemoServiceImpl表示DemoService服务的具体实现,此时的服务表示服务的具体实现
DemoService+group+version表示一个服务,此时的服务增加了分组和版本概念
http://192.168.1.112:80/com.luban.DemoService表示一个服务,此时的服务增加了机器IP和Port,表示远程机器可以访问这个URL来使用com.luban.DemoService这个服务
http://192.168.1.112:80/com.luban.DemoService?timeout=3000&version=1.0.1&application=dubbo-demo-provider-application表示一个服务,此时的服务是拥有参数的,比如超时时间、版本号、所属应用
服务导出要做的事情
1、更新服务参数,把服务参数确定好
public void onApplicationEvent(ContextRefreshedEvent event) {
// 当前服务没有被导出并且没有卸载,才导出服务
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
// 服务导出(服务注册)
export();
}
}
@Override
public void export() {
super.export();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
1、更新服务参数(前期准备工作)
在执行ServiceConfig.export()时,此时ServiceConfig对象就代表一个服务,我们已经知道了这个服务的名字(就是接口的名字),并且此时这个服务可能已经有一些参数了,就是**@Service注解上所定义的参数**。
但是在Dubbo中,除开可以在@Service注解中给服务配置参数,还有很多地方也可以给服务配置参数,比如:
- dubbo.properties文件,你可以建立这个文件,dubbo会去读取这个文件的内容作为服务的参数,Dubob的源码中叫做PropertiesConfiguration
- 配置中心,dubbo在2.7版本后就支持了分布式配置中心,你可以在Dubbo-Admin中去操作配置中心,分布式配置中心就相当于一个远程的dubbo.properties文件,你可以在Dubbo-Admin中去修改这个dubbo.properties文件,当然配置中心支持按应用进行配置,也可以按全局进行配置两种,在Dubbo的源码中AppExternalConfiguration表示应用配置,ExternalConfiguration表示全局配置。
- 系统环境变量,你可以在启动应用程序时,通过-D的方式来指定参数,在Dubbo的源码中叫SystemConfiguration
- 再加上通过@Service注解所配置的参数,在Dubbo的源码中叫AbstractConfig
服务的参数可以从这四个位置来,这四个位置上如果配了同一个参数的话,优先级从高到低如下:
SystemConfiguration** -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> **PropertiesConfiguration
可以看出,-D方式配置的参数优先级最高,配置中心次之,注解随后,dubbo.properties最后。
比如如果服务本身没有配置timeout参数,但是如果服务所属的应用的配置了timeout,那么这个应用下的服务都会继承这个timeout配置。
所以在确定服务参数时,需要先从上级获取参数,获取之后,如果服务本身配置了相同的参数,那么则进行覆盖。
那么我们来看看代码
public synchronized void export() {
// 把服务参数更新到最新
checkAndUpdateSubConfigs();
// 检查服务是否需要导出
if (!shouldExport()) {
return;
}
// 检查是否需要延迟发布
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
// 导出服务
doExport();
}
}
/**
* 1. ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
* 2. 从配置中心获取配置,包括应用配置和全局配置
* 3. 从配置中心获取Provider配置
* 4. 从配置中心获取Protocol配置
* 5. 如果ApplicationConfig为空,则构造一个ApplicationConfig
* 6. 从配置中心获取Registry配置
* 7. 更新ServiceConfig中的属性为优先级最高的配置
* 8. 更新MetadataReportConfig中的属性为优先级最高的配置
* 9. 检查当前服务是不是一个泛化服务
* 10.检查Stub和Local
* 11.检查Mock
*/
public void checkAndUpdateSubConfigs() {
// Use default configs defined explicitly on global configs
// ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
completeCompoundConfigs();
// Config Center should always being started first.
// 从配置中心获取配置,包括应用配置和全局配置
// 把获取到的配置放入到Environment中的externalConfigurationMap和appExternalConfigurationMap中
// 并刷新所有的Config属性
startConfigCenter();
// 如果没有ProviderConfig对象,则创建一个
checkDefault();
// 如果没有单独的配置protocols,那么就从provider获取配置的协议,添加到的ServiceConfig中去
// 假如程序员在配置文件中配了一个dubbo协议,配置中心的全局配置或应用配置中也配置了一个协议,那么就会被添加到ServiceConfig中
checkProtocol();
checkApplication();
// if protocol is not injvm checkRegistry
// 如果protocol不是只有injvm协议,表示服务调用不是只在本机jvm里面调用,那就需要用到注册中心
// 如果protocol是injvm,表示本地调用
if (!isOnlyInJvm()) {
checkRegistry();
}
// 刷新ServiceConfig
this.refresh();
// 如果配了metadataReportConfig,那么就刷新配置
checkMetadataReport();
if (StringUtils.isEmpty(interfaceName)) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
// 当前服务对应的实现类是一个GenericService,表示没有特定的接口
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
// 加载接口
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
// 刷新MethodConfig,并判断MethodConfig中对应的方法在接口中是否存在
checkInterfaceAndMethods(interfaceClass, methods);
// 实现类是不是该接口类型
checkRef();
generic = Boolean.FALSE.toString();
}
// local和stub一样,不建议使用了
if (local != null) {
// 如果本地存根为true,则存根类为interfaceName + "Local"
if (Boolean.TRUE.toString().equals(local)) {
local = interfaceName + "Local";
}
// 加载本地存根类
Class<?> localClass;
try {
localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
// 本地存根
if (stub != null) {
// 如果本地存根为true,则存根类为interfaceName + "Stub"
if (Boolean.TRUE.toString().equals(stub)) {
stub = interfaceName + "Stub";
}
Class<?> stubClass;
try {
stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
}
}
// 检查local和stub
checkStubAndLocal(interfaceClass);
// 检查mock
checkMock(interfaceClass);
}
1.1、覆盖已经存在的配置
注意这里走的是ServiceBean类!!!
这里什么意思呢,就是在容器内部,我们创建了一些配置,但是有一些配置 我们没有配置,那么就覆盖一下
private void completeCompoundConfigs() {
// 如果配置了provider,那么则从provider中获取信息赋值其他属性,在这些属性为空的情况下
if (provider != null) {
if (application == null) {
setApplication(provider.getApplication());
}
if (module == null) {
setModule(provider.getModule());
}
if (registries == null) {
setRegistries(provider.getRegistries());
}
if (monitor == null) {
setMonitor(provider.getMonitor());
}
if (protocols == null) {
setProtocols(provider.getProtocols());
}
if (configCenter == null) {
setConfigCenter(provider.getConfigCenter());
}
}
// 如果配置了module,那么则从module中获取信息赋值其他属性,在这些属性为空的情况下
if (module != null) {
if (registries == null) {
setRegistries(module.getRegistries());
}
if (monitor == null) {
setMonitor(module.getMonitor());
}
}
// 如果配置了application,那么则从application中获取信息赋值其他属性,在这些属性为空的情况下
if (application != null) {
if (registries == null) {
setRegistries(application.getRegistries());
}
if (monitor == null) {
setMonitor(application.getMonitor());
}
}
}
这里很简单,如果当前容器provider配置存在,那么你的application配置不存在,那么就把provider配置给application配置
当然这里调用的ServiceBean
1.2、注册中心获取配置,并更新配置
void startConfigCenter() {
// 获取注册中心
if (configCenter == null) {
ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
}
// 如果配置了ConfigCenter
if (this.configCenter != null) {
// 从其他位置获取配置中心的相关属性信息,比如配置中心地址
// TODO there may have duplicate refresh
this.configCenter.refresh();
// 属性更新后,从远程配置中心获取数据(应用配置,全局配置)
prepareEnvironment();
}
// 从配置中心取到配置数据后,刷新所有的XxConfig中的属性
ConfigManager.getInstance().refreshAll();
}
这里需要注意 获取注册中心
1.2.1、获取配置中心
这里我们在dubbo配置中配置中心了配置中心
那么就会在Spring内部有一个ConfigCenterConfig这个对象
// 获取注册中心
if (configCenter == null) {
ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
}
然后利用这里赋值给ServiceBean的configCenter
1.2.2、远程配置获取并存储
private void prepareEnvironment() {
if (configCenter.isValid()) {
if (!configCenter.checkOrUpdateInited()) {
return;
}
// 动态配置中心,管理台上的配置中心
DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());
// 如果是zookeeper,获取的就是/dubbo/config/dubbo/dubbo.properties节点中的内容
String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());
String appGroup = application != null ? application.getName() : null;
String appConfigContent = null;
if (StringUtils.isNotEmpty(appGroup)) {
// 获取的就是/dubbo/config/dubbo-demo-consumer-application/dubbo.properties节点中的内容
// 这里有bug
appConfigContent = dynamicConfiguration.getProperties
(StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
appGroup
);
}
try {
// 设置ServiceBean 待会刷新配置 咱们的优先级
// 如果setConfigCenterFirst == true
// 获取配置顺序如下
// SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
// 如果setConfigCenterFirst == false
// 获取配置顺序如下
// SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
// 这意味着如果为true,那么咱们自己在@Service配置的就在第四个获取
// 如果为false,那么咱们在@Service配置的就在第二个获取
Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
// 设置到全局map
Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
// 设置到服务级别Map
Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
} catch (IOException e) {
throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
}
}
}
这里流程是这样的
1、获取配置中心 DynamicConfiguration(zookeeper)
2、利用注册中心 从/dubbo/config/dubbo/dubbo.properties节点中的内容,并获取/dubbo/config/dubbo-demo-consumer-application/dubbo.properties节点内容
3、设置一下配置顺序,这个配置顺序 是关于我们@Service配置获取的顺序,一个第二 第一个第四
4、把/dubbo/config/dubbo/dubbo.properties内容设置到externalConfigurationMap里
5、把/dubbo/config/dubbo-demo-consumer-application/dubbo.properties内容设置到appExternalConfigurationMap里面
1.2.3、更新配置(不包含ServiceBean)
这里更新配置是不包含ServiceBean的
public void refreshAll() {
// refresh all configs here,
getApplication().ifPresent(ApplicationConfig::refresh);
getMonitor().ifPresent(MonitorConfig::refresh);
getModule().ifPresent(ModuleConfig::refresh);
getProtocols().values().forEach(ProtocolConfig::refresh);
getRegistries().values().forEach(RegistryConfig::refresh);
getProviders().values().forEach(ProviderConfig::refresh);
getConsumers().values().forEach(ConsumerConfig::refresh);
}
1.2.4、刷新ServiceConfig(ServiceBean)
// 刷新XxConfig
// 一个XxConfig对象的属性可能是有值的,也可能是没有值的,这时需要从其他位置获取属性值,来进行属性的覆盖
// 覆盖的优先级,从大到小为系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
// 以ServiceConfig为例,ServiceConfig中包括很多属性,比如timeout
// 但是在定义一个Service时,如果在注解上没有配置timeout,那么就会其他地方获取timeout的配置
// 比如可以从系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
// refresh是刷新,将当前ServiceConfig上的set方法所对应的属性更新为优先级最高的值
public void refresh() {
try {
// 注意这里
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
// 表示XxConfig对象本身- AbstractConfig
Configuration config = new ConfigConfigurationAdapter(this);
if (Environment.getInstance().isConfigCenterFirst()) {
// The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
compositeConfiguration.addConfiguration(4, config);
} else {
// The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
compositeConfiguration.addConfiguration(2, config);
}
// loop methods, get override value and set the new value back to method
//
Method[] methods = getClass().getMethods();
for (Method method : methods) {
// 是不是setXX()方法
if (MethodUtils.isSetter(method)) {
// 获取xx配置项的value
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
// isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
// 是不是setParameters()方法
} else if (isParametersSetter(method)) {
// 获取parameter配置项的value
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
if (StringUtils.isNotEmpty(value)) {
Map<String, String> map = invokeGetParameters(getClass(), this);
map = map == null ? new HashMap<>() : map;
map.putAll(convert(StringUtils.parseParameters(value), ""));
invokeSetParameters(getClass(), this, map);
}
}
}
} catch (Exception e) {
logger.error("Failed to override ", e);
}
}
这里只需要注意第一行代码
剩下的都是把Method给拆解然后 获取参数
这里我们主要注意这个方法
获取配置
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
这里俩个参数为:
public CompositeConfiguration getConfiguration(String prefix, String id) {
CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
// Config center has the highest priority
// JVM环境变量
compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
// 操作系统环境变量
compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));
// 配置中心APP配置
compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
// 配置中心Gloab配置
compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
// dubbo.properties中的配置
compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
return compositeConfiguration;
}
这里相当于 把所有服务咱们服务的参数都给拿出来
2、导出服务(前期准备工作)
咱们现在要干哪些事(假设只有一个要导出的协议)
1、组装要注册的URL
2、注册到注册中心
3、启动Netty
4、监听配置中心,如果有改动那么需要重新导出
public synchronized void export() {
// 把服务参数更新到最新
checkAndUpdateSubConfigs();
// 检查服务是否需要导出
if (!shouldExport()) {
return;
}
// 检查是否需要延迟发布
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
// 导出服务
doExport();
}
}
protected synchronized void doExport() {
// 当前服务已经被取消了,就不能再导出了,为什么?
if (unexported) {
throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
}
// 已经导出了,就不再导出了
if (exported) {
return;
}
exported = true;
if (StringUtils.isEmpty(path)) {
path = interfaceName;
}
doExportUrls();
}
2.1、获取注册URL
private void doExportUrls() {
// 得到url,注册服务也是一个服务,所以也会有对应的url,通过调用该url完成服务注册
List<URL> registryURLs = loadRegistries(true); //
// 遍历每个协议
// 一个协议一个服务
for (ProtocolConfig protocolConfig : protocols) {
// path表示服务名
// contextPath表示应用名(可配置)
// pathKey = group/contextpath/path:version
// 例子:myGroup/user/org.apache.dubbo.demo.DemoService:1.0.1
String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
// ProviderModel中存在服务提供者访问路径,实现类,接口,以及接口中的各个方法对应的ProviderMethodModel
// ProviderMethodModel表示某一个方法,方法名,所属的服务的,
ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
// ApplicationModel表示应用中有哪些服务提供者和引用了哪些服务
ApplicationModel.initProviderModel(pathKey, providerModel);
// 每种协议导出一个单独的服务,注册到各个注册中心
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
这里其实就相于把注册中心转换成url
protected List<URL> loadRegistries(boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
if (CollectionUtils.isNotEmpty(registries)) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
// 如果注册中心没有配地址,则地址为0.0.0.0
if (StringUtils.isEmpty(address)) {
address = ANYHOST_VALUE;
}
// 如果注册中心的地址不是"N/A"
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
// 把application中的参数放入map中,注意,map中的key是没有prefix的
appendParameters(map, application);
// 把config中的参数放入map中,注意,map中的key是没有prefix的
// config是RegistryConfig,表示注册中心
appendParameters(map, config);
// 此处path值固定为RegistryService.class.getName(),因为现在是在加载注册中心
map.put(PATH_KEY, RegistryService.class.getName());
// 把dubbo的版本信息和pid放入map中
appendRuntimeParameters(map);
// 如果map中如果没有protocol,那么默认为dubbo
if (!map.containsKey(PROTOCOL_KEY)) {
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
}
// 构造注册中心url,地址+参数
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = URLBuilder.from(url)
.addParameter(REGISTRY_KEY, url.getProtocol())
.setProtocol(REGISTRY_PROTOCOL)
.build();
// 到此为止,url的内容大概为:
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider
// &dubbo=2.0.2&pid=269936®istry=zookeeper×tamp=1584886077813
// 该url表示:使用registry协议调用org.apache.dubbo.registry.RegistryService服务
// 参数为application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936®istry=zookeeper×tamp=1584886077813
// 这里是服务提供者和服务消费者区别的逻辑
// 如果是服务提供者,获取register的值,如果为false,表示该服务不注册到注册中心
// 如果是服务消费者,获取subscribe的值,如果为false,表示该引入的服务不订阅注册中心中的数据
if ((provider && url.getParameter(REGISTER_KEY, true))
|| (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
如果有多个注册中心,那么就会生成多个URL
我们看一下这个url
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?
application=dubbo-demo-provider1-application
&dubbo=2.0.2
&logger=log4j
&pid=31242
®istry=zookeeper
&release=2.7.0
×tamp=1695022673057
变成这种url
2.2、遍历协议
为什么要遍历协议,因为一个协议就要注册一次到注册中心
private void doExportUrls() {
// 得到url,注册服务也是一个服务,所以也会有对应的url,通过调用该url完成服务注册
List<URL> registryURLs = loadRegistries(true); //
// 遍历每个协议
// 一个协议一个服务
for (ProtocolConfig protocolConfig : protocols) {
// path表示服务名
// contextPath表示应用名(可配置)
// pathKey = group/contextpath/path:version
// 例子:myGroup/user/org.apache.dubbo.demo.DemoService:1.0.1
String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
// ProviderModel中存在服务提供者访问路径,实现类,接口,以及接口中的各个方法对应的ProviderMethodModel
// ProviderMethodModel表示某一个方法,方法名,所属的服务的,
ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
// ApplicationModel表示应用中有哪些服务提供者和引用了哪些服务
ApplicationModel.initProviderModel(pathKey, providerModel);
// 每种协议导出一个单独的服务,注册到各个注册中心
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
这里协议只有一个我们配置的dubbo
2.3、组装URL
这里没有啥好说的就是拼接参数 组装
最后生成这么个东西
// dubbo://192.168.3.145:20881/org.apache.dubbo.demo.DemoService
// ?anyhost=true
// &application=dubbo-demo-provider1-application
// &bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.1.1:g1
// &bind.ip=192.168.3.145
// &bind.port=20881&deprecated=false
// &dubbo=2.0.2
// &dynamic=true
// &generic=false
// &group=g1
// &interface=org.apache.dubbo.demo.DemoService
// &logger=log4j
// &methods=sayHello
// &pid=31467
// &release=2.7.0
// &revision=1.1.1
// &sayHello.loadbalance=random
// &sayHello.return=true
// &side=provider
// &timeout=3000
// ×tamp=1695023256184
// &version=1.1.1
2.4、生成代理对象
这里我把代码给优化了
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
拼接参数...
代码优化...
// export service
// 通过该host和port访问该服务
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
// 服务url
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
// url:http://192.168.40.17:80/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=80&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=285072&release=&side=provider×tamp=1585206500409
// 可以通过ConfiguratorFactory,在服务导出时候进行统一配置
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
String scope = url.getParameter(SCOPE_KEY); // scope可能为null,remote, local,none
// don't export when none is configured
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 如果scope为none,则不会进行任何的服务导出,既不会远程,也不会本地
// export to local if the config is not remote (export to remote only when config is remote)
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
// 如果scope不是remote,则会进行本地导出,会把当前url的protocol改为injvm,然后进行导出
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
// 如果scope不是local,则会进行远程导出
if (CollectionUtils.isNotEmpty(registryURLs)) {
// 如果有注册中心,则将服务注册到注册中心
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
// 如果是injvm,则不需要进行注册中心注册
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
// 该服务是否是动态,对应zookeeper就是是否是临时节点,对应dubbo中的功能就是静态服务
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
// 基于注册中心地址的到监控中心地址,为什么是基于注册中心地址?
URL monitorUrl = loadMonitor(registryURL);
// 把监控中心地址添加到服务url中
if (monitorUrl != null) {
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
// 服务的register参数,如果为true,则表示要注册到注册中心
if (logger.isInfoEnabled()) {
if (url.getParameter(REGISTER_KEY, true)) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
} else {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
}
// For providers, this is used to enable custom proxy to generate invoker
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
// 生成一个当前服务接口的代理对象
// 使用代理生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
// 对应的url为 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&export=http%3A%2F%2F192.168.40.17%3A80%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D80%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D19472%26release%3D%26side%3Dprovider%26timestamp%3D1585207994860&pid=19472®istry=zookeeper×tamp=1585207994828
// 这个Invoker中包括了服务的实现者、服务接口类、服务的注册地址
// 在Dubbo的源码中需要屏蔽具体的服务类型
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
// 1. 先使用RegistryProtocol进行服务注册
// 2. 注册完了之后,使用DubboProtocol进行导出
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
// 没有配置注册中心时,也会导出服务
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
// 下面的代码和存在注册中心时一样,代码虽然一样,但是生成invoker的url是不一样的
// 当存在注册中心时,是先使用Registy协议注册服务,然后在使用Http协议导出服务
// 而没有注册中心时,是直接使用Http协议导出服务
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
/**
* @since 2.7.0
* ServiceData Store
*/
// 根据服务url,讲服务的元信息存入元数据中心
MetadataReportService metadataReportService = null;
if ((metadataReportService = getMetadataReportService()) != null) {
metadataReportService.publishProvider(url);
}
}
}
this.urls.add(url);
}
这里生成代理对象,就一句话
// 生成一个当前服务接口的代理对象
// 使用代理生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
// 对应的url为 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&export=http%3A%2F%2F192.168.40.17%3A80%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D80%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D19472%26release%3D%26side%3Dprovider%26timestamp%3D1585207994860&pid=19472®istry=zookeeper×tamp=1585207994828
// 这个Invoker中包括了服务的实现者、服务接口类、服务的注册地址
// 在Dubbo的源码中需要屏蔽具体的服务类型
// ref == 我们实例对象(我们测试的DemoServiceImpl)
// interfaceClass == 我们实例对象继承的接口(我们测试的DemoService)
// url == 根据咱们服务生成的url
// 但是这里注意 传递进去的是注册中心url是 registery:// 这个协议开头的
// 这里相当于把url当成一个参数(export) 放到了registery://...(注册中心url)后面
// url == dubbo://192.168.3.145:20881/org.apache.dubbo.demo.DemoService
// ?anyhost=true
// &application=dubbo-demo-provider1-application
// &bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.1.1:g1
// &bind.ip=192.168.3.145
// &bind.port=20881&deprecated=false
// &dubbo=2.0.2
// &dynamic=true
// &generic=false
// &group=g1
// &interface=org.apache.dubbo.demo.DemoService
// &logger=log4j
// &methods=sayHello
// &pid=31467
// &release=2.7.0
// &revision=1.1.1
// &sayHello.loadbalance=random
// &sayHello.return=true
// &side=provider
// &timeout=3000
// ×tamp=1695023256184
// &version=1.1.1
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
这里需要看一下我们PROXY_FACTORY.getInvoker()这个方法的参数
ref == 咱们的实例对象
interfaceCLass == 咱们的实例对象继承的接口
registerURL == 咱们的注册中心,但是后面有一个参数export 放着我们服务生成的url
2.4.1、JavassistProxyFactory处理创建AbstractProxyInvoker
这个PROXY_FACTORY默认我们没有处理,所以Dubbo的SPI会创建JavassistProxyFactory这个对象处理
public class JavassistProxyFactory extends AbstractProxyFactory {
@Override
@SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
// 如果现在被代理的对象proxy本身就是一个已经被代理过的对象,那么则取代理类的Wrapper,否则取type
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
// 执行proxy的method方法
// 为什么要用wrapper,因为方便,这里不是代理接口,是直接基于某个对象proxy,进行代理
// 执行的proxy实例的方法
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}
这里实际上嵌套了俩层
AbstractProxyInvoker
–调用doInvoke方法的时候
–Wrapper.invokeMethod
2.4.2、DelegateProviderMetaDataInvoker封装
当我们创建完毕后,这个时候我们会获取一个AbstractProxyInvoker这个Invoker,然后在利用DelegateProviderMetaDataInvoker把 AbstractProxyInvoker 和 ServiceBean封装到里面
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
DelegateProviderMetaDataInvoker
AbstractProxyInvoker
Wrapper(里面保存我们真正的服务实例)
3、导出服务(RegisterProtocol导出)
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
// 1. 先使用RegistryProtocol进行服务注册
// 2. 注册完了之后,使用DubboProtocol进行导出
Exporter<?> exporter = protocol.export(wrapperInvoker);
这里注意,我们刚刚上面已经了解到DelegateProviderMetaDataInvoker里面封装咱们Invoker,那么这个Invoker的URL就是咱们的上面AbstractProxyInvoker保存的url,registery://开头,那么Dubbo的SPI就会根据这个registery来找到RegisteryProtocol这个协议类
3.1、导出之前经历的Wrapper类
在经历调用到RegisteryProcol会经过俩个Wrapper类
ProtocolListenerWrapper(先执行)
ProtocolFilterWrapper(后执行)
这俩个类暂时都不会有任何作用,都是放行的
3.1.1、ProtocolListenerWrapper处理RegisteryProtocol导出
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),
// 得到ExporterListener接口中能用的扩展点,根据url和EXPORTER_LISTENER_KEY进行筛选
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
}
3.1.2、ProtocolFilterWrapper处理RegisteryProtocol导出
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
3.2、RegisterProtocol导出
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// originInvoker就是DelegateProviderMetaDataInvoker,表示一个服务提供者的Invoker
// 将registry://xxx?xx=xx®istry=zookeeper 转为 zookeeper://xxx?xx=xx
URL registryUrl = getRegistryUrl(originInvoker); // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.40.17%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D27656%26release%3D2.7.0%26side%3Dprovider%26timeout%3D3000%26timestamp%3D1590735956489&logger=log4j&pid=27656&release=2.7.0×tamp=1590735956479
// url to export locally 服务提供者url
URL providerUrl = getProviderUrl(originInvoker); // dubbo://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=27656&release=2.7.0&side=provider&timeout=3000×tamp=1590735956489
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
// the same service. Because the subscribed is cached key with the name of the service, it causes the
// subscription information to cover.
// overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
// 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
// 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
// OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
// 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
// providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
// serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
// 这两个监听器都是新版本中的监听器
// 新版本监听的zk路径是:
// 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
// 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
// 注意,要喝配置中心的路径区分开来,配置中心的路径是:
// 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
// 全局:/dubbo/config/dubbo/dubbo.properties节点的内容
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// export invoker
// 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
// 得到注册中心-ZookeeperRegistry
final Registry registry = getRegistry(originInvoker);
// 得到存入到注册中心去的providerUrl,会对服务提供者url中的参数进行简化
final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
// 将当前服务提供者Invoker,以及该服务对应的注册中心地址,以及简化后的服务url存入ProviderConsumerRegTable
ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
registryUrl, registeredProviderUrl);
//to judge if we need to delay publish
//是否需要注册到注册中心
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 注册服务,把简化后的服务提供者url注册到registryUrl中去
register(registryUrl, registeredProviderUrl);
providerInvokerWrapper.setReg(true);
}
// 针对老版本的动态配置,需要把overrideSubscribeListener绑定到overrideSubscribeUrl上去进行监听
// 兼容老版本的配置修改,利用overrideSubscribeListener去监听旧版本的动态配置变化
// 监听overrideSubscribeUrl provider://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=416332&release=&side=provider×tamp=1585318241955
// 那么新版本的providerConfigurationListener和serviceConfigurationListener是在什么时候进行订阅的呢?在这两个类构造的时候
// Deprecated! Subscribe to override rules in 2.6.x or before.
// 老版本监听的zk路径是:/dubbo/org.apache.dubbo.demo.DemoService/configurators/override://0.0.0.0/org.apache.dubbo.demo.DemoService?category=configurators&compatible_config=true&dynamic=false&enabled=true&timeout=6000
// 监听的是路径的内容,不是节点的内容
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<>(exporter);
}
这里我们逐步解析
1、获取注册中心URL,获取真实服务URL
2、创建监听器
3.2.1、获取注册中心URL,获取真实服务URL
// 将registry://xxx?xx=xx®istry=zookeeper 转为 zookeeper://xxx?xx=xx
URL registryUrl = getRegistryUrl(originInvoker); // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.40.17%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D27656%26release%3D2.7.0%26side%3Dprovider%26timeout%3D3000%26timestamp%3D1590735956489&logger=log4j&pid=27656&release=2.7.0×tamp=1590735956479
// 服务提供者url,就是放在原本url当参数(export)那段 服务URL
URL providerUrl = getProviderUrl(originInvoker);
3.2.2、创建监听器
// overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
// 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
// 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
// OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
1、把原本服务url(dubbo:// 开头) 重新生成了一个 以provider:// 开头的,并增加参数category=configurators&check=false
2、创建一个OverrideListener也就是监听器
3、把重新生成的服务url(provider://) + 监听器,存到这个OverrideListers这个Map里
3.2.3、应用监听器和服务监听器
// 此时的providerUrl == 是dubbo:// 开头的原本服务生成的URL
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
核心
private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
// 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
// providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
// serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
// 这两个监听器都是新版本中的监听器
// 新版本监听的zk路径是:
// 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
// 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
// 注意,要喝配置中心的路径区分开来,配置中心的路径是:
// 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
// 全局:/dubbo/config/dubbo/dubbo.properties节点的内容
// 应用配置,providerConfigurationListener是在属性那里直接初始化好的,providerConfigurationListener会监听配置中心的应用配置信息变动
providerUrl = providerConfigurationListener.overrideUrl(providerUrl);
// 服务配置,new ServiceConfigurationListener的时候回初始化,ServiceConfigurationListener会监听配置中心的服务信息配置信息变动
ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
return serviceConfigurationListener.overrideUrl(providerUrl);
}
这里分俩个监听器
providerConfigurationListener:创建RegisteryProtocol就创建了这个这个监听器(应用级别监听器)
ServiceConfigurationListener:导出的时候才创建(服务界别监听器)
3.2.3.1、ProviderConfigurationListener应用级别监听器处理
这个监听器在RegisterProtocol初始化的时候才创建出来
info 初始化
public ProviderConfigurationListener() {
// // 订阅 应用名+".configurators"
this.initWith(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX);
}
这里有init方法我们得看一下
// 在构造ProviderConfigurationListener和ServiceConfigurationListener都会调用到这个方法
// 完成Listener自身订阅到对应的应用和服务
// 订阅关系绑定完了之后,主动从动态配置中心获取一下对应的配置数据生成configurators,后面需要重写providerUrl
protected final void initWith(String key) {
// 获取注册中心
DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
// 添加Listener,进行了订阅
// 往Zookeeper添加监听器,这个this是ProviderConfigurationListener
dynamicConfiguration.addListener(key, this);
// 从配置中心ConfigCenter获取属于当前应用的动态配置数据,从zk中拿到原始数据(主动从配置中心获取数据)
String rawConfig = dynamicConfiguration.getRule(key, DynamicConfiguration.DEFAULT_GROUP);
// 如果存在应用配置信息则根据配置信息生成Configurator
if (!StringUtils.isEmpty(rawConfig)) {
genConfiguratorsFromRawRule(rawConfig);
}
}
1、获取注册中心(Zookeeper)
2、把自己当成监听器注册到注册中心
3、从配置中心获取数据,并把数据生成Configurator
3.2.3.1.1、监听器注册
info 这里我们看一下怎么注册的
default void addListener(String key, ConfigurationListener listener) {
addListener(key, DEFAULT_GROUP, listener);
}
key:dubbo-demo-provider1-application.configurators
DEFAULT_GROUP:dubbo
public void addListener(String key, String group, ConfigurationListener listener) {
// getPathKey(group, key) = /dubbo/config/dubbo/dubbo-demo-provider1-application.configurators
cacheListener.addListener(getPathKey(group, key), listener);
}
private Map<String, Set<ConfigurationListener>> keyListeners = new ConcurrentHashMap<>();
public void addListener(String key, ConfigurationListener configurationListener) {
//这里创建了一个CopyOnWrite的把key和监听器都放到这个Map里
Set<ConfigurationListener> listeners = this.keyListeners.computeIfAbsent(key, k -> new CopyOnWriteArraySet<>());
listeners.add(configurationListener);
}
这里就是注册到一个Map里,key:Set<监听器>
info 当来修改事件的时候,就会调用
public void dataChanged(String path, Object value, EventType eventType) {
if (eventType == null) {
return;
}
if (eventType == EventType.INITIALIZED) {
initializedLatch.countDown();
return;
}
if (path == null || (value == null && eventType != EventType.NodeDeleted)) {
return;
}
// TODO We only care the changes happened on a specific path level, for example
// /dubbo/config/dubbo/configurators, other config changes not in this level will be ignored,
if (path.split("/").length >= MIN_PATH_DEPTH) {
String key = pathToKey(path);
ConfigChangeType changeType;
switch (eventType) {
case NodeCreated:
changeType = ConfigChangeType.ADDED;
break;
case NodeDeleted:
changeType = ConfigChangeType.DELETED;
break;
case NodeDataChanged:
changeType = ConfigChangeType.MODIFIED;
break;
default:
return;
}
ConfigChangeEvent configChangeEvent = new ConfigChangeEvent(key, (String) value, changeType);
Set<ConfigurationListener> listeners = keyListeners.get(path);
if (CollectionUtils.isNotEmpty(listeners)) {
// 这里来处理
listeners.forEach(listener -> listener.process(configChangeEvent));
}
}
}
public void process(ConfigChangeEvent event) {
if (logger.isInfoEnabled()) {
logger.info("Notification of overriding rule, change type is: " + event.getChangeType() +
", raw config content is:\n " + event.getValue());
}
//
if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
configurators.clear();
} else {
if (!genConfiguratorsFromRawRule(event.getValue())) {
return;
}
}
notifyOverrides();
}
// 调用到我们ProviderConfigurationListener
@Override
protected void notifyOverrides() {
overrideListeners.values().forEach(listener -> ((OverrideListener) listener).doOverrideIfNecessary());
}
3.2.3.1.2、overrideUrl方法处理URL
private <T> URL overrideUrl(URL providerUrl) {
// 通过configurators去修改/装配providerUrl
return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl);
}
private static URL getConfigedInvokerUrl(List<Configurator> configurators, URL url) {
if (configurators != null && configurators.size() > 0) {
for (Configurator configurator : configurators) {
url = configurator.configure(url);
}
}
return url;
}
这里就是相当于 监听器监听的配置 来修改url
3.2.3.2、Dubbo是如何利用Zookeeper来实现的监听(动态配置)
注意Dubbo是利用Zookeeper来Watch机制来监听的,不过它监听的是dubbo/config这个里面的所有的变化,如果我们改动了就会调用到Watch,这个Watch会根据path来判断该调用哪个监听器,我们在注册监听器的时候会设置一个path
3.2.3.3、ServiceConfigurationListener服务级别监听器处理
private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
// 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
// providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
// serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
// 这两个监听器都是新版本中的监听器
// 新版本监听的zk路径是:
// 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
// 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
// 注意,要喝配置中心的路径区分开来,配置中心的路径是:
// 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
// 全局:/dubbo/config/dubbo/dubbo.properties节点的内容
// 应用配置,providerConfigurationListener是在属性那里直接初始化好的,providerConfigurationListener会监听配置中心的应用配置信息变动
providerUrl = providerConfigurationListener.overrideUrl(providerUrl);
// 服务配置,new ServiceConfigurationListener的时候回初始化,ServiceConfigurationListener会监听配置中心的服务信息配置信息变动
ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
return serviceConfigurationListener.overrideUrl(providerUrl);
}
这个类创建流程和ProviderConfigurationListener一样,不过他监听的是服务的
/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators
是这个地址的
然后放到这个serviceConfigurationListeners这个Map里
然后overrideUrl()这个方法执行流程和ProviderConfigurationListener一样,他也是修改url
3.2.4、DubboProtocol服务导出
// export invoker
// 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// protocol属性的值是哪来的,是在SPI中注入进来的,是一个代理类
// 这里实际利用的就是DubboProtocol或HttpProtocol去export NettyServer
// 为什么需要ExporterChangeableWrapper?方便注销已经被导出的服务
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
这里从bounds,这个Map里获取,没有就执行这个里面的lambda表达式
此时providerUrl是dubbo://开头的
这里把providerUrl 和 Invoker封装到这个invokerDelegate,然后进行服务导出
这里protocol.export()就会调用到DubboProtocol这个export方法里
不过在调用之前还会经历Wrapper类
3.2.4.1、DubboProtocol经历的Wrapper类
在经历调用到RegisteryProcol会经过俩个Wrapper类
ProtocolListenerWrapper(后执行)
ProtocolFilterWrapper(先执行)
3.2.4.1.1、ProtocolFilterWrapper处理
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
这里会经历一个buildInvoker方法 来封装这个invoker
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
// 根据url获取filter,根据url中的parameters取key为key的value所对应的filter,但是还会匹配group
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
// ConsumerContextFilter--->FutureFilter--->MonitorFilter
// ConsumerContextFilter用来设置RpcContext
//
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
// 得到一个异步结果
asyncResult = filter.invoke(next, invocation);
} catch (Exception e) {
// onError callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onError(e, invoker, invocation);
}
}
throw e;
}
return asyncResult;
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return new CallbackRegistrationInvoker<>(last, filters);
}
这里会把8个filter都封装 到一个CallbackRegistrationInvoker
CallbackRegistrationInvoker
filter:8个
invoker:InvokerDelegate
InvokerDelegate
DelegateProviderMetaDataInvoker
AbstractProxyInvoker
Wrapper(里面保存我们真正的服务实例
3.2.4.1.2、ProtocolListenerWrapper处理
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),
// 得到ExporterListener接口中能用的扩展点,根据url和EXPORTER_LISTENER_KEY进行筛选
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
}
这里是后续处理,当DubboProtocol导出完毕后,就会把DubboProtocol返回的DubboExporter给封装
封装成一个ListenerExporterWrapper,并且会利用SPI找ExporterListener这个类,给一并封装进去
3.2.4.2、DubboProtocol导出逻辑
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
// 构造一个Exporter
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);
//export an stub service for dispatching event
Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
if (isStubSupportEvent && !isCallbackservice) {
String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
if (logger.isWarnEnabled()) {
logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
"], has set stubproxy support event ,but no stub methods founded."));
}
} else {
// 服务的stub方法
stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
}
}
// 开启NettyServer
openServer(url);
optimizeSerialization(url);
return exporter;
}
1、获取url,此时的url是dubbo://
2、获取ServiceKey(组 + 服务名 + 版本 + 端口号)
g1/org.apache.dubbo.demo.DemoService:1.1.1:20881
3、把Invoker 和 ServiceKey 封装成DubboExporter
4、把这个DubboExporter 放入Map<ServiceKey,DubboExporter>
为什么要放入这个Map因为,客户端来请求的时候就可以带url来转换成ServiceKey,然后找到DubboExporter,就可以执行真正的方法Invoker了
5、// 开启NettyServer
到此导出结束
4、复盘逻辑
4.1、服务端Invoker架构
- ProtocolFilterWrapper$CallbackRegistrationInvoker:会去调用下层Invoker,下层Invoker执行完了之后,会遍历过滤器,查看是否有过滤器实现了ListenableFilter接口,如果有,则回调对应的onResponse方法,比如TimeoutFilter,当调用完下层Invoker之后,就会计算服务的执行时间
- ProtocolFilterWrapper$1:ProtocolFilterWrapper中的过滤器组成的Invoker,利用该Invoker,可以执行服务端的过滤器,执行完过滤器之后,调用下层Invoker
- RegistryProtocol$InvokerDelegate:服务的的委托类,里面包含了DelegateProviderMetaDataInvoker对象和服务对应的providerUrl,执行时直接调用下层Invoker
- DelegateProviderMetaDataInvoker:服务的的委托类,里面包含了AbstractProxyInvoker对象和ServiceConfig对象,执行时直接调用下层Invoker
- AbstractProxyInvoker:服务接口的代理类,绑定了对应的实现类,执行时会利用反射调用服务实现类实例的具体方法,并得到结果
4.2、Exporter架构

一个服务导出成功后,会生成对应的Exporter:
- DestroyableExporter:Exporter的最外层包装类,这个类的主要作用是可以用来unexporter对应的服务
- ExporterChangeableWrapper:这个类主要负责在unexport对应服务之前,把服务URL从注册中心中移除,把该服务对应的动态配置监听器移除
- ListenerExporterWrapper:这个类主要负责在unexport对应服务之后,把服务导出监听器移除
- DubboExporter:这个类中保存了对应服务的Invoker对象,和当前服务的唯一标志,当NettyServer接收到请求后,会根据请求中的服务信息,找到服务对应的DubboExporter对象,然后从对象中得到Invoker对象