Dubbo服务导出
难度系统 : ?????
Dubbo服务导出,调用ServiceBean#export(), 再去调用父类ServiceConfig#export()方法,进行服务真正导出;
Dubbo服务导出大体流程
- Dubbo的每个Service注解都对应一个ServiceBean对象,每个ServiceBean对象都实现了Spring的ApplicationListener接口,当Spring项目启动结束后,会触发一个上下文刷新事件ContextRefreshEvent事件, 触发的方法是onApplicationEvent方法, ServiceBean的onApplicationEvent方法中,会调用ServiceBean#export(), 再去调用父类ServiceConfig#export()方法进行服务导出
- 会确定每个ServiceBean的配置参数,像timeout, protocol等, 这些参数的配置可以在以下的地方进行配置;
2.1 JVM环境运行参数, 通过-D的方式来指定参数 2.2 配置中心可以配置 项目的全局配置和应用配置, 相当于一个分布式共享的Dubbo.properties文件; 2.3 @Service注解可以指定参数; 2.4 项目中, 指定的dubbo.properties配置文件; 这么多个地方可以指定配置参数, 在服务导出时,就需要确定每个Service使用的是哪些配置参数,即确定优先级最高的参数; - 加载确定注册中心信息;
- 根据配置的Protocol协议启动容器, Dubbo协议对应的Netty, http对应的是tomcat/jetty, 并在接收请求过程中,根据我们的配置接收请求, 如超时事件, 最大连接数等;
- 容器启动完后,每个ServiceBean的服务配置会注册到注册中心,同时会注册监听服务配置,监听配置的变更;
主要就是这几个过程, 更简单点 确定服务配置参数 => 启动容器 => 注册到注册中心;
服务概念
- 传统三层调用中,Service接口就是服务的定义,而ServiceImpl就是服务的具体是实现;dubbo也是如此;
- Service接口 + 分组group + 版本号version 更详细的定义了一个服务;
- ip:port/service接口:通过IP端口号远程访问一个服务;
- ip:port/service接口 ?group =xxx & version=xxx & timeout=xxx & … : 通过IP端口指定远程服务器, 通过service接口 + 接口配置参数更详细的定义了一个服务,确定服务的唯一性;
如:
127.0.0.1:8080/com.dubbo.demo.DemoService?group=user&version=1.0&timout=5000&applicationName=demo
dubbo使用的是第四种,来唯一定义一个Service服务;
服务导出代码流程总结
- 加载确定服务的配置参数;
- 加载注册中心URL信息;
- 构造服务最终的URL;
- 根据协议配置Protolcol启动不同的容器,用来接收请求;
- 服务URL注册到注册中心中;
- 监听服务配置信息,配置发生变更,则重新进行服务导出;
加载确定服务的配置参数
- 每个@Service对应一个ServiceBean服务配置实例, 而服务启动过程中,@DubboComponentScan注解执行完毕, 会把每个@Service转换一个个得ServiceBean实例, 并把@Service注解中的配置信息赋值给了ServiceBean实例,包括一些普通值timeout, 还有一些Dubbo配置, 如protocolConfig等;
- 在Dubbo中,除开可以在@Service注解中给服务配置参数,还有很多地方也可以给服务配置参数
- resouce资源文件dubbo.properties: Dubbo会去扫描这些配置文件, 加载为PropertiesConfiguration;
- 配置中心:一个分布式的properties文件, 包括两种,一种是全局配置,另一个种是应用配置;在Dubbo的源码中AppExternalConfiguration表示应用配置,ExternalConfiguration表示全局配置。
- @Service注解: 对应的AbstractConfig;
- JVM环境参数: 通过-D执行配置, 对应的类是SystemConfiguration;
-
如果这几处地方同时配置了同一个参数, 优先级由高到低为: SystemConfig > AppExternalConfiguration > ExternalConfiguration > AbstractConfig > PropertiesConfiguration 即-D方式配置的参数优先级最高,配置中心次之,注解随后,dubbo.properties最后。 -
因为配置参数同时可以在多个地方配置,所以服务导出的第一步就是确定服务的配置参数;
ServiceConfig#export()
public synchronized void export() {
checkAndUpdateSubConfigs();
doExport();
}
}
ServiceConfig#checkAndUpdateSubConfigs()
工作:
- completeCompoundConfigs补全配置参数
- 从配置中心获取应用配置和全局配置
- 获取ProviderConfig对象;
- 获取Protocol协议对象,默认为Dubbo协议;
- 获取ApplicationConfig对象;
- 获取Registry服务配置;
- 更新ServiceConfig 参数为最高优先级的配置;
- 检查当前服务是不是一个泛化服务
- 检查Stub和Local
- 检查Mock
public void checkAndUpdateSubConfigs() {
completeCompoundConfigs();
startConfigCenter();
checkDefault();
checkProtocol();
checkApplication();
if (!isOnlyInJvm()) {
checkRegistry();
}
this.refresh();
checkMetadataReport();
if (StringUtils.isEmpty(interfaceName)) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
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);
}
checkInterfaceAndMethods(interfaceClass, methods);
checkRef();
generic = Boolean.FALSE.toString();
}
if (local != null) {
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) {
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);
}
}
checkStubAndLocal(interfaceClass);
checkMock(interfaceClass);
}
ServiceConfig#completeCompoundConfigs()’
- 配置了provider, 而没有配置application, module, registeries, monitor ,protocols ,configCenter , 则从provider种获取这些配置;
- 配置了module,而没有配置registries, configCenter, 则从module中获取这些配置;
- 配置了application, 而没有配置registries , monitor ,则从application中获取这些配置;
private void completeCompoundConfigs() {
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());
}
}
if (module != null) {
if (registries == null) {
setRegistries(module.getRegistries());
}
if (monitor == null) {
setMonitor(module.getMonitor());
}
}
if (application != null) {
if (registries == null) {
setRegistries(application.getRegistries());
}
if (monitor == null) {
setMonitor(application.getMonitor());
}
}
}
ServiceConfig#startConfigCenter()
- 配置configCenter, 从其他位置获取配置中心的相关属性信息,比如配置中心地址
- 更新属性后, 从配置中获取全局配置,应用配置;
- 拿到配置后, 刷新所有XXXconfig的属性;
void startConfigCenter() {
if (configCenter == null) {
ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
}
if (this.configCenter != null) {
this.configCenter.refresh();
prepareEnvironment();
}
ConfigManager.getInstance().refreshAll();
}
ServiceConfig#refresh
工作:
- 获取所有相关的dubbo配置;
- 创建一个Configuration对象,代表AbstractConfig;
- isConfigCenterFirst的值默认为true,所以优先级的顺序为:系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件;
- 使用反射技术赋最高优先级别配置的值;
public void refresh() {
try {
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
Configuration config = new ConfigConfigurationAdapter(this);
if (Environment.getInstance().isConfigCenterFirst()) {
compositeConfiguration.addConfiguration(4, config);
} else {
compositeConfiguration.addConfiguration(2, config);
}
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} else if (isParametersSetter(method)) {
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);
}
}
配置中心配置确定优先级
Environment.getInstance().getConfiguration(getPrefix(), getId())
getInstance():单例饿汉式;
public class Environment {
private Map<String, PropertiesConfiguration> propertiesConfigs = new ConcurrentHashMap<>();
private Map<String, SystemConfiguration> systemConfigs = new ConcurrentHashMap<>();
private Map<String, EnvironmentConfiguration> environmentConfigs = new ConcurrentHashMap<>();
private Map<String, InmemoryConfiguration> externalConfigs = new ConcurrentHashMap<>();
private Map<String, InmemoryConfiguration> appExternalConfigs = new ConcurrentHashMap<>();
private static final Environment INSTANCE = new Environment();
public static Environment getInstance() {
return INSTANCE;
}
}
getConfiguration:获取配置,放入CompositeConfiguration 内部的List容器中;
- JVM环境变量
- 操作系统环境变量(我没接触过)
- 配置中心APP配置
- 配置中心Global全局配置
- dubbo.properties中的配置
- 返回compositeConfiguration实例;
可以看出分别获取,按顺序的就可以初步知道优先级了。 系统配置变量 > 配置中心 > dubbo文件配置;
public CompositeConfiguration getConfiguration(String prefix, String id) {
CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
return compositeConfiguration;
}
CompositeConfiguration类: 内部有一个List集合,存放配置;
public class CompositeConfiguration implements Configuration {
private List<Configuration> configList = new LinkedList<Configuration>();
}
Environment.getInstance().isConfigCenterFirst()
可以得知: 默认情况configCenterFirst的值为true;
public class Environment {
private static final Environment INSTANCE = new Environment();
private boolean configCenterFirst = true;
public boolean isConfigCenterFirst() {
return configCenterFirst;
}
}
因此,下面代码中, 往集合的第4个位置, 插入代表AbstractConfig的配置;
public void refresh() {
try {
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
Configuration config = new ConfigConfigurationAdapter(this);
if (Environment.getInstance().isConfigCenterFirst()) {
compositeConfiguration.addConfiguration(4, config);
} else {
compositeConfiguration.addConfiguration(2, config);
}
}
最终配置的优先级为: 系统配置变量 > 配置中心 > @Service注解配置 > dubbo文件配置;
配置中心配置赋最高优先级别的值
- 获取ServiceConfig配置类的所有方法
- 遍历Method
- 判断是否为setter方法;
- 如果是setter方法, 则使用compositeConfiguration.getString(extractPropertyName(getClass(), method)获取属性的值
4.1 遍历compositeConfiguration内部的已经排好序的Configuration集合List 4.2 Configuration判断是否包含了当前key, 包含就说明找到优先级最高的配置,返回; - 通过反射赋值;
- 如果不是setter方法, 是setParameters方法;
- 获取parameter配置项的value;
- 通过反射调用getParameters方法获取parameter值;
- 合并parameter的值, 通过反射调用setParameter方法赋值;
public void refresh() {
try {
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} else if (isParametersSetter(method)) {
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) {
}
}
prepareEnvironment()
目的: 获取配置中心的配置;
- 获取动态配置中心;
- 获取全局配置的内容configContent ;
- 获取管理组名;
- 如果存在管理组,则有App应用配置, 获取应用配置的内容appConfigContent ;
- 设置configCenterFirst属性值为true;
- 解析全局配置内容,并赋值给Environment#externalConfigurationMap;
- 解析应用配置内容, 并赋值给Environment#appExternalConfigurationMap
private void prepareEnvironment() {
if (configCenter.isValid()) {
if (!configCenter.checkOrUpdateInited()) {
return;
}
DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());
String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());
String appGroup = application != null ? application.getName() : null;
String appConfigContent = null;
if (StringUtils.isNotEmpty(appGroup)) {
appConfigContent = dynamicConfiguration.getProperties
(StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
appGroup
);
}
try {
Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
} catch (IOException e) {
throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
}
}
}
ConfigManager.getInstance().refreshAll()
上面的步骤只是刷新ConfigCenterConfig的最新值;而其他配置类例如ApplicationConfig, ProtocolConfig等的值不是最高优先级别的值, 因此这些配置类也需要刷新, 赋最高优先级别的值;
public void refreshAll() {
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);
}
代码写得非常漂亮, 使用反射,因此通用性很高; 过程类似ConfigCenterConfig, 不重复码了。
ServiceConfig#checkDefault()
目的: 如果没有ProviderConfig对象,则创建一个
private void checkDefault() {
createProviderIfAbsent();
}
private void createProviderIfAbsent() {
if (provider != null) {
return;
}
setProvider(
ConfigManager.getInstance()
.getDefaultProvider()
.orElseGet(() -> {
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.refresh();
return providerConfig;
})
);
}
public void setProvider(ProviderConfig provider) {
ConfigManager.getInstance().addProvider(provider);
this.provider = provider;
}
ServiceConfig#checkProtocol
目的:获取设置ProtocolConfig; 工作:
- 如果没有单独的配置protocols,那么就从provider获取配置的协议,添加到的ServiceConfig中去
- 假如程序员在配置文件中配了一个dubbo协议,配置中心的全局配置或应用配置中也配置了一个协议,那么就会被添加到ServiceConfig中
2.1 获取全局配置的protocol, 根据前缀"dubbo.protocols." 从externalConfigurationMap中获取; 2.2 获取应用配置的protocol, 根据前缀"dubbo.protocols." 从appExternalConfigurationMap中获取; 2.3 拼接配置; 2.4 如果配置中心没有配置,则使用默认的Dubbo协议配置, 创建一个默认的ProtocolConfig 赋值给ConfigManager管理; 2.5 如果配置中心有配置,则分别创建每个配置创建一个ProtocolConfig, 然后一起交给ConfigManager管理;
private void checkProtocol() {
if (CollectionUtils.isEmpty(protocols) && provider != null) {
setProtocols(provider.getProtocols());
}
convertProtocolIdsToProtocols();
}
private void convertProtocolIdsToProtocols() {
if (StringUtils.isEmpty(protocolIds) && CollectionUtils.isEmpty(protocols)) {
List<String> configedProtocols = new ArrayList<>();
configedProtocols.addAll(getSubProperties(Environment.getInstance()
.getExternalConfigurationMap(), PROTOCOLS_SUFFIX));
configedProtocols.addAll(getSubProperties(Environment.getInstance()
.getAppExternalConfigurationMap(), PROTOCOLS_SUFFIX));
protocolIds = String.join(",", configedProtocols);
}
if (StringUtils.isEmpty(protocolIds)) {
if (CollectionUtils.isEmpty(protocols)) {
setProtocols(
ConfigManager.getInstance().getDefaultProtocols()
.filter(CollectionUtils::isNotEmpty)
.orElseGet(() -> {
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.refresh();
return new ArrayList<>(Arrays.asList(protocolConfig));
})
);
}
} else {
String[] arr = COMMA_SPLIT_PATTERN.split(protocolIds);
List<ProtocolConfig> tmpProtocols = CollectionUtils.isNotEmpty(protocols) ? protocols : new ArrayList<>();
Arrays.stream(arr).forEach(id -> {
if (tmpProtocols.stream().noneMatch(prot -> prot.getId().equals(id))) {
tmpProtocols.add(ConfigManager.getInstance().getProtocol(id).orElseGet(() -> {
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setId(id);
protocolConfig.refresh();
return protocolConfig;
}));
}
});
if (tmpProtocols.size() > arr.length) {
throw new IllegalStateException("Too much protocols found, the protocols comply to this service are :" + protocolIds + " but got " + protocols
.size() + " registries!");
}
setProtocols(tmpProtocols);
}
}
checkApplication()
如果没有配置application,则创建一个ApplicationConfig实例,赋值给ConfigManager;
protected void checkApplication() {
createApplicationIfAbsent();
}
private void createApplicationIfAbsent() {
if (this.application != null) {
return;
}
ConfigManager configManager = ConfigManager.getInstance();
setApplication(
configManager
.getApplication()
.orElseGet(() -> {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.refresh();
return applicationConfig;
})
);
}
ServiceConfig#refresh()
- this.refresh()逻辑和ConfigCenterConfig的刷新代码逻辑一样的;
- ServiceConfig对象的属性可能是有值的,也可能是没有值的,这时需要从其他位置获取属性值,来进行属性的覆盖
- ServiceConfig本身有很多配置, 通过刷新获取最高优先级的配置值;例如timeout, 如果注解上没有配置这个值,就会从其他地方获取配置;
- 覆盖的优先级,从大到小为系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
- 将当前ServiceConfig上的set方法所对应的属性更新为优先级最高的值
public void refresh() {
try {
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
Configuration config = new ConfigConfigurationAdapter(this);
if (Environment.getInstance().isConfigCenterFirst()) {
compositeConfiguration.addConfiguration(4, config);
} else {
compositeConfiguration.addConfiguration(2, config);
}
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} else if (isParametersSetter(method)) {
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);
}
}
到这里,主要的服务导出确定服务配置参数过程就完了。代码写得非常nice, 通用性非常强;
总结
确定配置参数仅仅只是服务导出的第一步, 反射技术还是必须要会的技术, 基本框架底层都使用, 任重倒运;
|