Dubbo源码---服务导出
Dubbo服务导出在ServiceConfig中export方法,整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装 URL。第二部分是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。第三部分是向注册中心注册服务,用于服务发现。本篇文章将会对这三个部分代码进行详细的分析。
前置工作
实例化DubboBootstrap
public static DubboBootstrap getInstance() {
if (instance == null) {
synchronized (DubboBootstrap.class) {
if (instance == null) {
instance = new DubboBootstrap();
}
}
}
return instance;
}
private DubboBootstrap() {
// 获取ConfigManager
configManager = ApplicationModel.getConfigManager();
// 获取Environment
environment = ApplicationModel.getEnvironment();
// 注册DubboShutdownHook
DubboShutdownHook.getDubboShutdownHook().register();
ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() {
@Override
public void callback() throws Throwable {
DubboBootstrap.this.destroy();
}
});
}
DubboBootstrap在实例化阶段做了三件事,第一件事是获取ConfigManager,第二件事是获取Enviroment,第三件事注册DubboShutdownHook,当Dubbo服务关闭时起到清理作用。
初始化DubboBootstrap
public void initialize() {
if (!initialized.compareAndSet(false, true)) {
return;
}
// 初始化框架扩展
ApplicationModel.initFrameworkExts();
// 启动配置中心
startConfigCenter();
// 加载远程调用配置
loadRemoteConfigs();
// 校验通用配置
checkGlobalConfigs();
// @since 2.7.8
// 启动元数据中心
startMetadataCenter();
// 初始化元数据服务
initMetadataService();
// 初始化元数据服务导出
initMetadataServiceExports();
// 初始化事件监听器
initEventListener();
if (logger.isInfoEnabled()) {
logger.info(NAME + " has been initialized!");
}
}
初始化FramewrokExt
public static void initFrameworkExts() {
// 此处为获取扩展类实例,即从ExtensionLoader中获取其实例
Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
for (FrameworkExt ext : exts) {
// 初始化FrameworkExt
ext.initialize();
}
}
通过Dubbo SPI获取FrameworkExt的扩展类,并进行初始化。有ConfigManager、Environment、ServiceRepository,其中ConfigManager、ServiceRepository执行的是默认的空方法,关注下Environment的初始化。
public void initialize() throws IllegalStateException {
// 获取ConfigManager
ConfigManager configManager = ApplicationModel.getConfigManager();
// 获取默认的配置中心
Optional<Collection<ConfigCenterConfig>> defaultConfigs = configManager.getDefaultConfigCenter();
// 遍历配置中心,并设置外部配置及应用外部配置
defaultConfigs.ifPresent(configs -> {
for (ConfigCenterConfig config : configs) {
this.setExternalConfigMap(config.getExternalConfiguration());
this.setAppExternalConfigMap(config.getAppExternalConfiguration());
}
});
this.externalConfiguration.setProperties(externalConfigurationMap);
this.appExternalConfiguration.setProperties(appExternalConfigurationMap);
}
Environment中的initialize方法,主要是获取配置中心,从这里可以看出,一个Dubbo应用,是可以同时存在多个配置中心的,并从配置中心的配置中获取参数设置到Environment中。
启动ConfigCenter
private void startConfigCenter() {
// 如果未在environment及ConfigManager设置配置中心,且ConfigManager中添加了RegistryConfig,则将ConfigManager中的Registry设置为配置中心
useRegistryAsConfigCenterIfNecessary();
// 获取配置中心
Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();
// 校验配置中心
if (CollectionUtils.isEmpty(configCenters)) {
ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
// 刷新ConfigCenterConfig
configCenterConfig.refresh();
if (configCenterConfig.isValid()) {
// 添加配置中心
configManager.addConfigCenter(configCenterConfig);
configCenters = configManager.getConfigCenters();
}
} else {
for (ConfigCenterConfig configCenterConfig : configCenters) {
// 刷新配置中心
configCenterConfig.refresh();
// 校验配置中心配置
ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
}
}
if (CollectionUtils.isNotEmpty(configCenters)) {
// 多配置中心管理类的实例
CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration();
for (ConfigCenterConfig configCenter : configCenters) {
// 将配置中心添加到多配置中心管理器
compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
}
environment.setDynamicConfiguration(compositeDynamicConfiguration);
}
// 刷新所有配置
configManager.refreshAll();
}
配置中心的启动分为了配置中心获取、校验配置中心、刷新配置中心、多配置中心管理、刷新ConfigManager中其中config配置。我们首先来看下useRegistryAsConfigCenterIfNecessary方法。
private void useRegistryAsConfigCenterIfNecessary() {
// 我们通过DynamicConfiguration的加载状态来判断ConfigCenter是否已经启动
if (environment.getDynamicConfiguration().isPresent()) {
return;
}
// ConfigManager中的configCenters为空
if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) {
return;
}
// 添加配置中心
configManager
// 获取默认的Restry
.getDefaultRegistries()
.stream()
// 过滤出使用Restry作为ConfigCenter的配置
.filter(this::isUsedRegistryAsConfigCenter)
// 将Registry转换成ConfigCenter
.map(this::registryAsConfigCenter)
// 最终的结果添加到ConfigManager的configCenters
.forEach(configManager::addConfigCenter);
}
useRegistryAsConfigCenterIfNecessary方法很简单,就是判断是否需要把Registry设置为ConfigCenter,如果需要,则将Registr转换成ConfigCenter。回到这里继续往下看,需要判断useRegistryAsConfigCenterIfNecessary方法是否成功设置configCenter,如果没有,则需要创建一个并刷新。再来看下refresh方法:
public void refresh() {
Environment env = ApplicationModel.getEnvironment();
try {
CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
// 循环方法,获取覆盖值并将新值设置回方法
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
try {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
// 调用 isTypeMatch() 是为了避免重复和错误更新,例如,我们在 ReferenceConfig 中有两个“setGeneric”方法。
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} catch (NoSuchMethodException e) {
logger.info("Failed to override the property " + method.getName() + " in " +
this.getClass().getSimpleName() +
", please make sure every property has getter/setter method provided.");
}
} 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);
}
}
从refresh这个方法可以看到,其主要逻辑是通过Environment获取配置文件信息,然后通过反射方式进行属性的setter注入。继续回到这里往下看是处理多配置中心以及刷新其余的配置。
加载RemoteConfig
private void loadRemoteConfigs() {
// 将registry ids转换成RegistryConfig
List<RegistryConfig> tmpRegistries = new ArrayList<>();
Set<String> registryIds = configManager.getRegistryIds();
registryIds.forEach(id -> {
if (tmpRegistries.stream().noneMatch(reg -> reg.getId().equals(id))) {
tmpRegistries.add(configManager.getRegistry(id).orElseGet(() -> {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setId(id);
// 刷新注册配置
registryConfig.refresh();
return registryConfig;
}));
}
});
// 添加注册配置(RegistryConfig)
configManager.addRegistries(tmpRegistries);
// 将protocol ids转换成ProtocolConfig
List<ProtocolConfig> tmpProtocols = new ArrayList<>();
Set<String> protocolIds = configManager.getProtocolIds();
protocolIds.forEach(id -> {
if (tmpProtocols.stream().noneMatch(prot -> prot.getId().equals(id))) {
tmpProtocols.add(configManager.getProtocol(id).orElseGet(() -> {
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setId(id);
// protocolConfig刷新
protocolConfig.refresh();
return protocolConfig;
}));
}
});
// 添加协议配置(ProtocolConfig)
configManager.addProtocols(tmpProtocols);
}
加载RemoteConfig的按流程看下来,获取ConfigManager中的protocoIds、registryIds,并将他们转换成ProtocolConfig及RegistryConfig并刷新,最后将它们交给ConfigManager管理。
校验通用配置
通用配置的代码比较简单,流程化的校验,下面简单的把代码贴出来,不做过多的分析。
private void checkGlobalConfigs() {
// 校验ApplicationConfig(应用配置)
ConfigValidationUtils.validateApplicationConfig(getApplication());
// 校验MetadataReportConfig(元数据导出配置)
Collection<MetadataReportConfig> metadatas = configManager.getMetadataConfigs();
if (CollectionUtils.isEmpty(metadatas)) {
MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
// 刷新
metadataReportConfig.refresh();
if (metadataReportConfig.isValid()) {
configManager.addMetadataReport(metadataReportConfig);
metadatas = configManager.getMetadataConfigs();
}
}
if (CollectionUtils.isNotEmpty(metadatas)) {
for (MetadataReportConfig metadataReportConfig : metadatas) {
// 刷新
metadataReportConfig.refresh();
// 校验
ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
}
}
// 校验ProviderConfig(提供者配置)
Collection<ProviderConfig> providers = configManager.getProviders();
if (CollectionUtils.isEmpty(providers)) {
configManager.getDefaultProvider().orElseGet(() -> {
ProviderConfig providerConfig = new ProviderConfig();
configManager.addProvider(providerConfig);
// 刷新
providerConfig.refresh();
return providerConfig;
});
}
for (ProviderConfig providerConfig : configManager.getProviders()) {
ConfigValidationUtils.validateProviderConfig(providerConfig);
}
// 校验ConsumerConfig(消费者配置)
Collection<ConsumerConfig> consumers = configManager.getConsumers();
if (CollectionUtils.isEmpty(consumers)) {
configManager.getDefaultConsumer().orElseGet(() -> {
ConsumerConfig consumerConfig = new ConsumerConfig();
configManager.addConsumer(consumerConfig);
consumerConfig.refresh();
return consumerConfig;
});
}
for (ConsumerConfig consumerConfig : configManager.getConsumers()) {
ConfigValidationUtils.validateConsumerConfig(consumerConfig);
}
// 校验MonitoryConfig(监控配置)
ConfigValidationUtils.validateMonitorConfig(getMonitor());
// 校验MetricsConfig
ConfigValidationUtils.validateMetricsConfig(getMetrics());
// 校验ModuleConfig
ConfigValidationUtils.validateModuleConfig(getModule());
// 校验SslConfig
ConfigValidationUtils.validateSslConfig(getSsl());
}
流程化的校验,逻辑很简单,这里不做赘述。
检查ApplciationConfig
public static void validateApplicationConfig(ApplicationConfig config) {
if (config == null) {
return;
}
// <dubbo:application name="">校验name是否为null
if (!config.isValid()) {
throw new IllegalStateException("No application config found or it's not a valid config! " +
"Please add <dubbo:application name=\"...\" /> to your spring config.");
}
// 在系统配置中设置dubbo service关闭等待事件
String wait = ConfigUtils.getProperty(SHUTDOWN_WAIT_KEY);
if (wait != null && wait.trim().length() > 0) {
System.setProperty(SHUTDOWN_WAIT_KEY, wait.trim());
} else {
wait = ConfigUtils.getProperty(SHUTDOWN_WAIT_SECONDS_KEY);
if (wait != null && wait.trim().length() > 0) {
System.setProperty(SHUTDOWN_WAIT_SECONDS_KEY, wait.trim());
}
}
// dubbo服务命名规则检查
checkName(NAME, config.getName());
// 检查应用程序所有者
checkMultiName(OWNER, config.getOwner());
// 检查应用程序的组织(BU)
checkName(ORGANIZATION, config.getOrganization());
// 检查架构层
checkName(ARCHITECTURE, config.getArchitecture());
// 检查环境
checkName(ENVIRONMENT, config.getEnvironment());
// 检查自定义参数
checkParameterName(config.getParameters());
}
检查MetadataReportConfig
public static void validateMetadataConfig(MetadataReportConfig metadataReportConfig) {
if (metadataReportConfig == null) {
return;
}
}
检查ProviderConfig
public static void validateProviderConfig(ProviderConfig config) {
// 检查上下文路径
checkPathName(CONTEXTPATH_KEY, config.getContextpath());
// 检查线程池
checkExtension(ThreadPool.class, THREADPOOL_KEY, config.getThreadpool());
// 检查telnet
checkMultiExtension(TelnetHandler.class, TELNET, config.getTelnet());
// 检查状态检查
checkMultiExtension(StatusChecker.class, STATUS_KEY, config.getStatus());
// 检查Transport
checkExtension(Transporter.class, TRANSPORTER_KEY, config.getTransporter());
// 检查Exchange
checkExtension(Exchanger.class, EXCHANGER_KEY, config.getExchanger());
}
检查ConsumerConfig
public static void validateConsumerConfig(ConsumerConfig config) {
if (config == null) {
return;
}
}
检查MonitoryConfig
public static void validateMonitorConfig(MonitorConfig config) {
if (config != null) {
if (!config.isValid()) {
logger.info("There's no valid monitor config found, if you want to open monitor statistics for Dubbo, " +
"please make sure your monitor is configured properly.");
}
checkParameterName(config.getParameters());
}
}
检查MetricsConfig
public static void validateMetricsConfig(MetricsConfig metricsConfig) {
if (metricsConfig == null) {
return;
}
}
检查ModuleConfig
public static void validateModuleConfig(ModuleConfig config) {
if (config != null) {
// 校验Module名称
checkName(NAME, config.getName());
// 校验Module所有者
checkName(OWNER, config.getOwner());
// 校验Module organization
checkName(ORGANIZATION, config.getOrganization());
}
}
检查SslConfig
public static void validateSslConfig(SslConfig sslConfig) {
if (sslConfig == null) {
return;
}
}
在校验通用配置这个模块中,我们发现有很多配置的refresh操作,我们接下来分析下这个方法:
public void refresh() {
Environment env = ApplicationModel.getEnvironment();
try {
CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
// 循环方法,获取覆盖值并将新值设置回方法
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
try {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
// 调用 isTypeMatch() 是为了避免重复和错误更新,例如,我们在 ReferenceConfig 中有两个“setGeneric”方法。
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} catch (NoSuchMethodException e) {
logger.info("Failed to override the property " + method.getName() + " in " +
this.getClass().getSimpleName() +
", please make sure every property has getter/setter method provided.");
}
} 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);
}
}
整个方法看起来感觉是有点难度,简单总结起来就是,在配置中心(包括不限于如nacos、apollo、system、propertis)获取对应的配置信息,并注入到对象中。
启动MetadataCenter
private void startMetadataCenter() {
// 如果有需要,使用registry作为元数据中心
useRegistryAsMetadataCenterIfNecessary();
ApplicationConfig applicationConfig = getApplication();
String metadataType = applicationConfig.getMetadataType();
// FIXME, multiple metadata config support.
Collection<MetadataReportConfig> metadataReportConfigs = configManager.getMetadataConfigs();
if (CollectionUtils.isEmpty(metadataReportConfigs)) {
// 校验metadataType是否为remote
if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
throw new IllegalStateException("No MetadataConfig found, you must specify the remote Metadata Center address when 'metadata=remote' is enabled.");
}
return;
}
MetadataReportConfig metadataReportConfig = metadataReportConfigs.iterator().next();
// 校验
ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
if (!metadataReportConfig.isValid()) {
return;
}
// 初始化
MetadataReportInstance.init(metadataReportConfig.toUrl());
}
这里也是流程化的逻辑,不是很难,不做详细赘述,要注意init方法,设置了URL的协议模式;此外,此处根据ApplicationConfig的metadataType是本地或远程,如果选择远程,需要进一步指定元数据中心。
public static void init(URL metadataReportURL) {
if (init.get()) {
return;
}
// 获取MetadataReportFactory扩展
MetadataReportFactory metadataReportFactory = ExtensionLoader.getExtensionLoader(MetadataReportFactory.class).getAdaptiveExtension();
// 如果metadataReportURL的protocol为metadata
if (METADATA_REPORT_KEY.equals(metadataReportURL.getProtocol())) {
// 如果URL中的protocol为null,则使用默认的dubbo
String protocol = metadataReportURL.getParameter(METADATA_REPORT_KEY, DEFAULT_DIRECTORY);
metadataReportURL = URLBuilder.from(metadataReportURL)
.setProtocol(protocol)
.removeParameter(METADATA_REPORT_KEY)
.build();
}
// 通过自适应扩展获取MetadataReport
metadataReport = metadataReportFactory.getMetadataReport(metadataReportURL);
init.set(true);
}
MetadataReportInstance的init方法就是通过Dubbo SPI获取其扩展,并指定元数据中心。
初始化MetadataService
private void initMetadataServiceExports() {
this.metadataServiceExporters = getExtensionLoader(MetadataServiceExporter.class).getSupportedExtensionInstances();
}
MetadataService的初始化很简单,通过Dubbo SPI获取org.apache.dubbo.metadata.MetadataServiceExporter配置文件下的所有扩展。
初始化事件监听器
将DubboBootstrap添加到EventDispatcher(事件调度器)中
校验及更新子配置
private void checkAndUpdateSubConfigs() {
// 使用默认配置显式定义的全局作用域
completeCompoundConfigs();
// 校验默认配置,如果默认的provider为null则新创建一个
checkDefault();
// 校验协议,将protocolIds转换成Protocol
checkProtocol();
// 初始化一些空配置。
List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
.getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
configInitializers.forEach(e -> e.initServiceConfig(this));
// if protocol is not injvm checkRegistry
// 如果protocol不是injvm,则需要校验
if (!isOnlyInJvm()) {
// 校验注册表
checkRegistry();
}
// 刷新
this.refresh();
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);
}
// 检查远程服务接口和方法是否符合Dubbo的要求。主要检查配置文件中配置的方法是否包含在远程服务的接口中
checkInterfaceAndMethods(interfaceClass, getMethods());
// 校验Ref
checkRef();
generic = Boolean.FALSE.toString();
}
if (local != null) {
if ("true".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 ("true".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);
}
}
// 检查stub和local
checkStubAndLocal(interfaceClass);
// 检查mock 本地模拟操作的合法性检查和设置。操作可以是带有简单操作的字符串,也可以是其 {@link Class} 实现特定功能的类名
ConfigValidationUtils.checkMock(interfaceClass, this);
// 检查ServiceConfig
ConfigValidationUtils.validateServiceConfig(this);
// Config配置的后置处理
postProcessConfig();
}
服务导出
多协议多注册中心导出服务
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)) {
// 将interfaceName赋值给path
path = interfaceName;
}
// 导出
doExportUrls();
}
private void doExportUrls() {
ServiceRepository repository = ApplicationModel.getServiceRepository();
// 注册服务到ServiceRepository
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
// 注册Provider到ServiceRepository
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
// 加载注册表
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
// 遍历ProtocolConfig集合导出每个服务。并在导出服务的过程中,将服务注册到注册中心
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
// 如果用户指定了路径,则再次注册服务,将其映射到路径。
repository.registerService(pathKey, interfaceClass);
// TODO, uncomment this line once service key is unified
serviceMetadata.setServiceKey(pathKey);
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
上面的代码先是分别将服务和Provider注册到ServiceRepository,然后通过loadRegistries加载注册中心链接,最后遍历ProtocolConfig导出每个服务,并在导出服务的过程中,将服务注册到注册中心。
public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
// 获取ApplicationConfig
ApplicationConfig application = interfaceConfig.getApplication();
// 获取RegistryConfig
List<RegistryConfig> registries = interfaceConfig.getRegistries();
if (CollectionUtils.isNotEmpty(registries)) {
for (RegistryConfig config : registries) {
// 获取服务注册地址
String address = config.getAddress();
if (StringUtils.isEmpty(address)) {
// 若为null,设置为0.0.0.0
address = ANYHOST_VALUE;
}
// 如果不为N/A类型
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
// 添加 ApplicationConfig 中的字段信息到 map 中
AbstractConfig.appendParameters(map, application);
// 添加 RegistryConfig 字段信息到 map 中
AbstractConfig.appendParameters(map, config);
// 添加 path、pid,protocol 等信息到 map 中
map.put(PATH_KEY, RegistryService.class.getName());
AbstractInterfaceConfig.appendRuntimeParameters(map);
if (!map.containsKey(PROTOCOL_KEY)) {
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
}
// 解析得到URL列表,address可能包含多个注册中心ip,
// 因此解析得到的是一个 URL 列表
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = URLBuilder.from(url)
.addParameter(REGISTRY_KEY, url.getProtocol())
// 将URL协议头设置为 registry
.setProtocol(extractRegistryType(url))
.build();
// 通过判断条件,决定是否添加 url 到 registryList 中,条件如下:
// (服务提供者 && register = true 或 null)
// || (非服务提供者 && subscribe = true 或 null)
if ((provider && url.getParameter(REGISTER_KEY, true))
|| (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
loadRegistries 方法主要包含如下的逻辑:
- 检测是否存在注册中心配置类,不存在则抛出异常
- 构建参数映射集合,也就是 map
- 构建注册中心链接列表
- 遍历链接列表,并根据条件决定是否将其添加到 registryList 中
URL组装
配置检查完毕后,紧接着要做的事情是根据配置,以及其他一些信息组装 URL。因为Dubbo的基础是根据Dubbo SPI来设计的,所以URL贯穿来整个Dubbo,接下来继续分析doExportUrlsFor1Protocol方法
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
String name = protocolConfig.getName();
// 如果协议名为空,或空串,则将协议名变量设置为 dubbo
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
// 通过反射将对象的字段信息添加到 map 中
ServiceConfig.appendRuntimeParameters(map);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
AbstractConfig.appendParameters(map, provider);
AbstractConfig.appendParameters(map, protocolConfig);
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
// methods 为 MethodConfig 集合,MethodConfig 中存储了 <dubbo:method> 标签的配置信息
if (CollectionUtils.isNotEmpty(getMethods())) {
for (MethodConfig method : getMethods()) {
// 添加 MethodConfig 对象的字段信息到 map 中,键 = 方法名.属性名。
// 比如存储 <dubbo:method name="sayHello" retries="2"> 对应的 MethodConfig,
// 键 = sayHello.retries,map = {"sayHello.retries": 2, "xxx": "yyy"}
AbstractConfig.appendParameters(map, method, method.getName());
String retryKey = method.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
// 检测 MethodConfig retry 是否为 false,若是,则设置重试次数为0
if ("false".equals(retryValue)) {
map.put(method.getName() + ".retries", "0");
}
}
// 获取 ArgumentConfig 列表
List<ArgumentConfig> arguments = method.getArguments();
if (CollectionUtils.isNotEmpty(arguments)) {
for (ArgumentConfig argument : arguments) {
// // 检测 type 属性是否为空,或者空串
if (argument.getType() != null && argument.getType().length() > 0) {
Method[] methods = interfaceClass.getMethods();
// visit all methods
if (methods.length > 0) {
for (int i = 0; i < methods.length; i++) {
String methodName = methods[i].getName();
// 比对方法名,查找目标方法
if (methodName.equals(method.getName())) {
Class<?>[] argtypes = methods[i].getParameterTypes();
// one callback in the method
if (argument.getIndex() != -1) {
// 检测 ArgumentConfig 中的 type 属性与方法参数列表中的参数名称是否一致,不一致则抛出异常
if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
// 添加 ArgumentConfig 字段信息到 map 中,
// 键前缀 = 方法名.index,比如:
// map = {"sayHello.3": true}
AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
} else {
throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
} else {
// multiple callbacks in the method
for (int j = 0; j < argtypes.length; j++) {
Class<?> argclazz = argtypes[j];
// 从参数类型列表中查找类型名称为 argument.type 的参数
if (argclazz.getName().equals(argument.getType())) {
AbstractConfig.appendParameters(map, argument, method.getName() + "." + j);
if (argument.getIndex() != -1 && argument.getIndex() != j) {
throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
}
}
}
}
}
}
// 用户未配置 type 属性,但配置了 index 属性,且 index != -1
} else if (argument.getIndex() != -1) {
// 添加 ArgumentConfig 字段信息到 map 中
AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
} else {
throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
}
}
}
} // end of methods for
}
// 检测 generic 是否为 "true",并根据检测结果向 map 中添加不同的信息
if (ProtocolUtils.isGeneric(generic)) {
map.put(GENERIC_KEY, generic);
map.put(METHODS_KEY, ANY_VALUE);
} else {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
// 为接口生成包裹类 Wrapper,Wrapper 中包含了接口的详细信息,比如接口方法名数组,字段信息等
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
// 添加方法名到 map 中,如果包含多个方法名,则用逗号隔开,比如 method = init,destroy
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
// 将逗号作为分隔符连接方法名,并将连接后的字符串放入 map 中
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
// 添加 token 到 map 中
if(ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
// 随机生成 token
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}
// 初始化ServiceMetadata中的attachments
serviceMetadata.getAttachments().putAll(map);
// 获取host
String host = findConfigedHosts(protocolConfig, registryURLs, map);
// 获取port
Integer port = findConfigedPorts(protocolConfig, name, map);
// 组装URL
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
// 您可以自定义Configurator以附加额外参数
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);
// don't export when none is configured
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 导出服务到本地
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 导出服务到注册中心
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
// 加载监视器链接
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
// 将监视器链接作为参数添加到 url 中
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
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);
}
// 为服务提供类(ref)生成 Invoker
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 导出服务,并生成 Exporter,此处的PROTOCOL为Dubbo SPI生成的自适应扩展类
// 再不配置其他包装类的情况下,首先会执行ProtocolFilterWrapper的export,再执行ProtocolListenerWrapper的export
// 最后执行RegistryProtocol的export
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
// 不存在注册中心,仅导出服务
} else {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
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
*/
WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
if (metadataService != null) {
metadataService.publishServiceDefinition(url);
}
}
}
this.urls.add(url);
}
上面的代码首先是将一些信息,比如版本、时间戳、方法名以及各种配置对象的字段信息放入到 map 中,map 中的内容将作为 URL 的查询字符串。构建好 map 后,紧接着是获取上下文路径、主机名以及端口号等信息。最后将 map 和主机名等数据传给 URL 构造方法创建 URL 对象。需要注意的是,这里出现的 URL 并非 java.net.URL,而是 com.alibaba.dubbo.common.URL。其中MethodConfig配置的解析嵌套太深,但无非就是根据配置解析完毕后,将对应的信息存储到map中。map构建完毕后,接下来就是根据map及serviceConfig组装URL。最后就是执行导出服务。
导出服务
我们再来看下doExportUrlsFor1Protocol方法,其根据 url 中的 scope 参数决定服务导出方式,分别如下:
- scope = none,不导出服务
- scope != remote,导出到本地
- scope != local,导出到远程
不管是导出到本地,还是远程。进行服务导出之前,均需要先创建 Invoker,这是一个很重要的步骤。因此下面先来分析 Invoker 的创建过程。先来分析下Invoker是如何创建的,再来分析服务导出.
Invoker创建
在 Dubbo 中,Invoker 是一个非常重要的模型。在服务提供端,以及服务引用端均会出现 Invoker。Dubbo 官方文档中对 Invoker 进行了说明,这里引用一下。
Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
既然 Invoker 如此重要,那么我们很有必要搞清楚 Invoker 的用途。Invoker 是由 ProxyFactory 创建而来,Dubbo 默认的 ProxyFactory 实现类是 JavassistProxyFactory。下面我们到 JavassistProxyFactory 代码中,探索 Invoker 的创建过程。如下:
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// 为目标类创建 Wrapper Wrapper类不能正确地处理这个场景:classname 包含'$'
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
// 创建匿名 Invoker 类对象,并实现 doInvoke 方法。
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
// 调用 Wrapper 的 invokeMethod 方法,invokeMethod 最终会调用目标方法
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
如上,JavassistProxyFactory 创建了一个继承自 AbstractProxyInvoker 类的匿名对象,并覆写了抽象方法 doInvoke。覆写后的 doInvoke 逻辑比较简单,仅是将调用请求转发给了 Wrapper 类的 invokeMethod 方法。Wrapper 用于“包裹”目标类,Wrapper 是一个抽象类,仅可通过 getWrapper(Class) 方法创建子类。在创建 Wrapper 子类的过程中,子类代码生成逻辑会对 getWrapper 方法传入的 Class 对象进行解析,拿到诸如类方法,类成员变量等信息。以及生成 invokeMethod 方法代码和其他一些方法代码。代码生成完毕后,通过 Javassist 生成 Class 对象,最后再通过反射创建 Wrapper 实例。相关的代码如下:
public static Wrapper getWrapper(Class<?> c) {
while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
{
c = c.getSuperclass();
}
if (c == Object.class) {
return OBJECT_WRAPPER;
}
Wrapper wrapper = WRAPPER_MAP.computeIfAbsent(c, key -> makeWrapper(key));
return wrapper;
}
getWrapper 方法仅包含一些缓存操作逻辑,不难理解。下面我们看一下 makeWrapper 方法。
private static Wrapper makeWrapper(Class<?> c) {
// 检测 c 是否为基本类型,若是则抛出异常
if (c.isPrimitive()) {
throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
}
String name = c.getName();
ClassLoader cl = ClassUtils.getClassLoader(c);
// c1 用于存储 setPropertyValue 方法代码
StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
// c2 用于存储 getPropertyValue 方法代码
StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
// c3 用于存储 invokeMethod 方法代码
StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
// 生成类型转换代码及异常捕捉代码,比如:
// DemoService w; try { w = ((DemoServcie) $1); }}catch(Throwable e){ throw new IllegalArgumentException(e); }
c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
// pts 用于存储成员变量名和类型
Map<String, Class<?>> pts = new HashMap<>();
// ms 用于存储方法描述信息(可理解为方法签名)及 Method 实例
Map<String, Method> ms = new LinkedHashMap<>();
// mns 为方法名列表
List<String> mns = new ArrayList<>();
// dmns 用于存储“定义在当前类中的方法”的名称
List<String> dmns = new ArrayList<>();
// 获取 public 访问级别的字段,并为所有字段生成条件判断语句
for (Field f : c.getFields()) {
String fn = f.getName();
Class<?> ft = f.getType();
if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) {
// 忽略关键字 static 或 transient 修饰的变量
continue;
}
// 生成条件判断及赋值语句,比如:
// if( $2.equals("name") ) { w.name = (java.lang.String) $3; return;}
// if( $2.equals("age") ) { w.age = ((Number) $3).intValue(); return;}
c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
// 生成条件判断及返回语句,比如:
// if( $2.equals("name") ) { return ($w)w.name; }
c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
// 存储 <字段名, 字段类型> 键值对到 pts 中
pts.put(fn, ft);
}
Method[] methods = c.getMethods();
// 检测 c 中是否包含在当前类中声明的方法
boolean hasMethod = hasMethods(methods);
if (hasMethod) {
c3.append(" try{");
for (Method m : methods) {
// 忽略 Object 中定义的方法
if (m.getDeclaringClass() == Object.class) {
continue;
}
String mn = m.getName();
// 生成方法名判断语句,比如:
// if ( "sayHello".equals( $2 )
c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
int len = m.getParameterTypes().length;
// 生成“运行时传入的参数数量与方法参数列表长度”判断语句,比如:
// && $3.length == 2
c3.append(" && ").append(" $3.length == ").append(len);
boolean override = false;
for (Method m2 : methods) {
// 检测方法是否存在重载情况,条件为:方法对象不同 && 方法名相同
if (m != m2 && m.getName().equals(m2.getName())) {
override = true;
break;
}
}
// 对重载方法进行处理,考虑下面的方法:
// 1. void sayHello(Integer, String)
// 2. void sayHello(Integer, Integer)
// 方法名相同,参数列表长度也相同,因此不能仅通过这两项判断两个方法是否相等。
// 需要进一步判断方法的参数类型
if (override) {
if (len > 0) {
for (int l = 0; l < len; l++) {
// 生成参数类型进行检测代码,比如:
// && $3[0].getName().equals("java.lang.Integer")
// && $3[1].getName().equals("java.lang.String")
c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
.append(m.getParameterTypes()[l].getName()).append("\")");
}
}
}
// 添加 ) {,完成方法判断语句,此时生成的代码可能如下(已格式化):
// if ("sayHello".equals($2)
// && $3.length == 2
// && $3[0].getName().equals("java.lang.Integer")
// && $3[1].getName().equals("java.lang.String")) {
c3.append(" ) { ");
// 根据返回值类型生成目标方法调用语句
if (m.getReturnType() == Void.TYPE) {
// w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]); return null;
c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
} else {
// return w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]);
c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
}
// 添加 }, 生成的代码形如(已格式化):
// if ("sayHello".equals($2)
// && $3.length == 2
// && $3[0].getName().equals("java.lang.Integer")
// && $3[1].getName().equals("java.lang.String")) {
//
// w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]);
// return null;
// }
c3.append(" }");
// 添加方法名到 mns 集合中
mns.add(mn);
// 检测当前方法是否在 c 中被声明的
if (m.getDeclaringClass() == c) {
// 若是,则将当前方法名添加到 dmns 中
dmns.add(mn);
}
ms.put(ReflectUtils.getDesc(m), m);
}
// 添加异常捕捉语句
c3.append(" } catch(Throwable e) { ");
c3.append(" throw new java.lang.reflect.InvocationTargetException(e); ");
c3.append(" }");
}
// 添加 NoSuchMethodException 异常抛出代码
c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
// 处理 get/set 方法
Matcher matcher;
for (Map.Entry<String, Method> entry : ms.entrySet()) {
String md = entry.getKey();
Method method = entry.getValue();
// 匹配以 get 开头的方法
if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
// 获取属性名
String pn = propertyName(matcher.group(1));
// 生成属性判断以及返回语句,示例如下:
// if( $2.equals("name") ) { return ($w).w.getName(); }
c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
pts.put(pn, method.getReturnType());
// 匹配以 is/has/can 开头的方法
} else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
String pn = propertyName(matcher.group(1));
// 生成属性判断以及返回语句,示例如下:
// if( $2.equals("dream") ) { return ($w).w.hasDream(); }
c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
pts.put(pn, method.getReturnType());
// 匹配以 set 开头的方法
} else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
Class<?> pt = method.getParameterTypes()[0];
String pn = propertyName(matcher.group(1));
// 生成属性判断以及 setter 调用语句,示例如下:
// if( $2.equals("name") ) { w.setName((java.lang.String)$3); return; }
c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt, "$3")).append("); return; }");
pts.put(pn, pt);
}
}
// 添加 NoSuchPropertyException 异常抛出代码
c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
// make class
long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
// 创建类生成器
ClassGenerator cc = ClassGenerator.newInstance(cl);
// 设置类名及超类
cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
cc.setSuperClass(Wrapper.class);
// 添加默认构造方法
cc.addDefaultConstructor();
// 添加方法代码
cc.addField("public static String[] pns;");
cc.addField("public static " + Map.class.getName() + " pts;");
cc.addField("public static String[] mns;");
cc.addField("public static String[] dmns;");
for (int i = 0, len = ms.size(); i < len; i++) {
cc.addField("public static Class[] mts" + i + ";");
}
cc.addMethod("public String[] getPropertyNames(){ return pns; }");
cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
cc.addMethod("public String[] getMethodNames(){ return mns; }");
cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
cc.addMethod(c1.toString());
cc.addMethod(c2.toString());
cc.addMethod(c3.toString());
try {
// 生成类
Class<?> wc = cc.toClass();
// 设置字段值
wc.getField("pts").set(null, pts);
wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
wc.getField("mns").set(null, mns.toArray(new String[0]));
wc.getField("dmns").set(null, dmns.toArray(new String[0]));
int ix = 0;
for (Method m : ms.values()) {
wc.getField("mts" + ix++).set(null, m.getParameterTypes());
}
// 创建 Wrapper 实例
return (Wrapper) wc.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
cc.release();
ms.clear();
mns.clear();
dmns.clear();
}
}
对于这种代码生成的方法,个人感觉没有太多要分析的,下面贴出一段由此方法生成的Wrappe示例如:
package org.apache.dubbo.demo.provider.test;
import org.apache.dubbo.common.bytecode.Wrapper;
public class Wrapper$sw0 extends Wrapper {
public static String[] pns;
public static java.util.Map pts;
public static String[] mns;
public static String[] dmns;
public static Class[] mts0;
public static Class[] mts1;
@Override
public String[] getPropertyNames(){
return pns;
}
@Override
public boolean hasProperty(String n){
return pts.containsKey(n);
}
@Override
public Class getPropertyType(String n){
return (Class)pts.get(n);
}
@Override
public String[] getMethodNames(){
return mns;
}
@Override
public String[] getDeclaredMethodNames(){
return dmns;
}
@Override
public void setPropertyValue(Object o, String n, Object v){
org.apache.dubbo.demo.DemoService w;
try{
w = ((org.apache.dubbo.demo.DemoService)o);
}catch(Throwable e){
throw new IllegalArgumentException(e);
}
throw new org.apache.dubbo.common.bytecode.NoSuchPropertyException("Not found property \""+n+"\" field or " + "setter method in class org.apache.dubbo.demo.DemoService.");
}
@Override
public Object getPropertyValue(Object o, String n){
org.apache.dubbo.demo.DemoService w;
try{
w = ((org.apache.dubbo.demo.DemoService)o);
}catch(Throwable e){
throw new IllegalArgumentException(e);
}
throw new org.apache.dubbo.common.bytecode.NoSuchPropertyException("Not found property \""+n+"\" field or " + "setter method in class org.apache.dubbo.demo.DemoService.");
}
@Override
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{
org.apache.dubbo.demo.DemoService w;
try{
w = ((org.apache.dubbo.demo.DemoService)o);
}catch(Throwable e){
throw new IllegalArgumentException(e);
}
try{
if( "sayHelloAsync".equals(n) && p.length == 1 ) {
return w.sayHelloAsync((String)v[0]);
}
if( "sayHello".equals(n) && p.length == 1 ) {
return w.sayHello((String)v[0]);
}
} catch(Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+ n +"\" in class " + "org.apache.dubbo.demo.DemoService."); }
}
导出服务到本地
private void exportLocal(URL url) {
URL local = URLBuilder.from(url)
// 设置为InjvmProtocol
.setProtocol(LOCAL_PROTOCOL)
.setHost(LOCALHOST_VALUE)
.setPort(0)
.build();
// 执行导出:先是执行Protocol的包装类,框架内置的有ProtocolFilterWrapper>ProtocolListenerWrapper
// 最后在优先级最低的包装类的export方法中执行导出,此处最后执行的是InjvmProtocol的export方法
Exporter<?> exporter = PROTOCOL.export(
// 通过ProxyFactory获取Invoker,默认为JavassistProxyFactory
PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local)
);
// 添加到缓存中
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}
这个方法做了以下几件事:
- 将传递过来的URL重新构建,将Protocol设置为InjvmProtocol,host设置为127.0.0.1,port设置为0
- 获取Invoker
- 执行export方法导出服务
- 将Exporter方法缓存
PROTOCOL为protocol的自适应扩展类,将根据URL的协议获取扩展类,这里获取到的InjvmProtocol,最后执行的顺序是(未添加其他Protocol的包装类的情况下):ProtocolFilterWrapper->ProtocolListenerWrapper->InjvmProtocol,我们再来看下InjvmProtocol的export方法
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// 构造InjvmExporter返回
return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
如上,InjvmProtocol 的 export 方法仅创建了一个 InjvmExporter,无其他逻辑。到此导出服务到本地就分析完了,接下来,我们继续分析导出服务到远程的过程。
导出服务到远程
同理,PROTOCOL为protocol的自适应扩展类,将根据URL的协议获取扩展类,这里获取到的RegistryProtocol,最后执行的顺序是(未添加其他Protocol的包装类的情况下):ProtocolFilterWrapper->ProtocolListenerWrapper->InjvmProtocol,我们再来看下RegistryProtocol的export方法:>
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// 获取注册中心 URL,以 zookeeper 注册中心为例,得到的示例 URL 如下:
// zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2
// &export=dubbo%3A%2F%2F172.17.48.52%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider
URL registryUrl = getRegistryUrl(originInvoker);
// 获取provider的url
// dubbo://192.168.1.101:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider
// &bind.ip=192.168.1.101&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true
// &generic=false&interface=org.apache.dubbo.demo.DemoService&metadata-type=remote&methods=sayHello,sayHelloAsync
// &pid=15770&release=&side=provider×tamp=1635326008251
URL providerUrl = getProviderUrl(originInvoker);
// 订阅URL,比如:
// provider://192.168.1.101:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider
// &bind.ip=192.168.1.101&bind.port=20880&category=configurators&check=false&default=true&deprecated=false
// &dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&metadata-type=remote
// &methods=sayHello,sayHelloAsync&pid=15770&release=&side=provider×tamp=1635326008251
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
// TODO
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// 导出Export
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry
final Registry registry = getRegistry(originInvoker);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// 是否需要延迟加载
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 向注册中心注册服务
register(registryUrl, registeredProviderUrl);
}
// 在提供程序模型上注册声明的url
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
// 弃用!订阅以覆盖2.6中的规则。x或之前。
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
notifyExport(exporter);
// 确保每次导出时都返回一个新的导出器实例
return new DestroyableExporter<>(exporter);
}
上面代码看起来比较复杂,主要做如下一些操作:
- 调用 doLocalExport 导出服务
- 向注册中心注册服务
- 向注册中心进行订阅 override 数据
- 创建并返回 DestroyableExporter
doLocalExport
在以上操作中,除了创建并返回 DestroyableExporter 没什么难度外,其他几步操作都不是很简单。这其中,导出服务和注册服务是本章要重点分析的逻辑。 订阅 override 数据并非本文重点内容,后面会简单介绍一下。下面先来分析 doLocalExport 方法的逻辑,如下:
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
// 访问并设置缓存
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
// 创建Invoker为委托对象
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// 执行Protocol的export方法,默认为DubboProtocol,可在ProtocolConfig中的设置name进行设置,如设置为RedisProtocol,:protocolConfig.setName("redis");
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
这里的逻辑看起来很简单,首先是构建Invoker实例,然后执行export方法,最后构建ExporterChangeableWrapper实例。这里需要关注的有两点,第一点是方法中protocol实例是哪种类型,第二点是具体执行的export方法。经过上面的分析我们知道doLocalExport的上一级方法为RegistryProtocol的export方法,RegistryProtocol的export上一级方法为ServiceConfig中的PROTOCOL.export方法,上面我们分析出ServiceConfig中的PROTOCOL为protocl的自适应扩展类,在该自适应扩展类中,解析URL获取到的protocol为registry,然后通过getExtension(“registry”)方法获取到扩展类RegistryProtocol,在SPI中我们分析过,Dubbo SPI之于java SPI多了一个IOC注入的过程,这里在获取到了RegistryProtocol后对其进行了IOC注入,最后RegistryProtocol的protocl注入到类型为Protocol的自适应扩展类,到这里我们就可以知道RegistryProtocol中的protocol为自适应扩展类,接着会在该扩展类中执行export方法,通过解析URL获取到具体Protocol的扩展,这里默认的为DubboProtocol,可修改ProtocolConfig中name属性进行修改,如设置为RedisProtocol:protocolConfig.setName("redis");,接下来分析DubboProtocol中的export方法:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如:
// demoGroup/com.alibaba.dubbo.demo.DemoService:1.0.1:20880
String key = serviceKey(url);
// 创建 DubboExporter
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// 将 <key, exporter> 键值对放入缓存中
exporterMap.put(key, exporter);
// 导出用于分派事件的存根服务
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."));
}
}
}
// 启动服务器
openServer(url);
// 优化序列化
optimizeSerialization(url);
return exporter;
}
我们重点关注 DubboExporter 的创建以及 openServer 方法,其他逻辑看不懂也没关系,不影响理解服务导出过程。另外,DubboExporter 的代码比较简单,就不分析了。下面分析 openServer 方法。
private void openServer(URL url) {
// 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
String key = url.getAddress();
// 客户端可以导出只供服务器调用的服务
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
// 访问缓存
ProtocolServer server = serverMap.get(key);
if (server == null) {
synchronized (this) {
// 访问缓存
server = serverMap.get(key);
if (server == null) {
// 创建服务
serverMap.put(key, createServer(url));
}
}
} else {
// 服务器已创建,则根据 url 中的配置重置服务器
server.reset(url);
}
}
}
如上,在同一台机器上(单网卡),同一个端口上仅允许启动一个服务器实例。若某个端口上已有服务器实例,此时则调用 reset 方法重置服务器的一些配置。考虑到篇幅问题,关于服务器实例重置的代码就不分析了。接下来分析服务器实例的创建过程。如下:
private ProtocolServer createServer(URL url) {
url = URLBuilder.from(url)
// 当服务器关闭时发送只读事件,默认为启用
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
// 默认启用心跳
.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
// 添加编码解码器参数
.addParameter(CODEC_KEY, DubboCodec.NAME)
.build();
// 获取 server 参数,默认为 netty
String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
// 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
}
ExchangeServer server;
try {
// 创建 ExchangeServer
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
// 获取 client 参数,可指定 netty,mina
str = url.getParameter(CLIENT_KEY);
if (str != null && str.length() > 0) {
// 获取所有的 Transporter 实现类名称集合,比如 supportedTypes = [netty, mina]
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
// 检测当前 Dubbo 所支持的 Transporter 实现类名称列表中,
// 是否包含 client 所表示的 Transporter,若不包含,则抛出异常
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return new DubboProtocolServer(server);
}
createServer 包含三个核心的逻辑。第一是检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常。第二是创建服务器实例。第三是检测是否支持 client 参数所表示的 Transporter 拓展,不存在也是抛出异常。两次检测操作所对应的代码比较直白了,无需多说。但创建服务器的操作目前还不是很清晰,我们继续往下看。
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
// 获取 Exchanger,默认为 HeaderExchanger。
// 紧接着调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例
return getExchanger(url).bind(url, handler);
}
上面代码比较简单,就不多说了。下面看一下 HeaderExchanger 的 bind 方法。
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
// 创建 HeaderExchangeServer 实例,该方法包含了多个逻辑,分别如下:
// 1. new HeaderExchangeHandler(handler)
// 2. new DecodeHandler(new HeaderExchangeHandler(handler))
// 3. Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
HeaderExchanger 的 bind 方法包含的逻辑比较多,但目前我们仅需关心 Transporters 的 bind 方法逻辑即可。该方法的代码如下:
public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handlers == null || handlers.length == 0) {
throw new IllegalArgumentException("handlers == null");
}
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else {
// 如果 handlers 元素数量大于1,则创建 ChannelHandler 分发器
handler = new ChannelHandlerDispatcher(handlers);
}
// 获取自适应 Transporter 实例,并调用实例方法
return getTransporter().bind(url, handler);
}
getTransporter() 方法获取的 Transporter 是在运行时动态创建的,类名为 TransporterAdaptive 会在运行时根据传入的 URL 参数决定加载什么类型的 Transporter,默认为 NettyTransporter。下面我们继续跟下去,这次分析的是 NettyTransporter 的 bind 方法。
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
// 创建 NettyServer
return new NettyServer(url, handler);
}
这里仅有一句创建 NettyServer 的代码,无需多说,我们继续向下看。
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// 您可以自定义客户端线程池的名称和类型:THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
// 处理程序将被包装: MultiMessageHandler->HeartbeatHandler->handler
// 调用父类构造方法
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
// 调用父类构造方法,这里就不用跟进去了,没什么复杂逻辑
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
// 获取 ip 和端口
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
// 设置 ip 为 0.0.0.0
bindIp = ANYHOST_VALUE;
}
bindAddress = new InetSocketAddress(bindIp, bindPort);
// 获取最大可接受连接数
this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
try {
// 调用模板方法 doOpen 启动服务器
doOpen();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
}
} catch (Throwable t) {
throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
+ " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
}
executor = executorRepository.createExecutorIfAbsent(url);
}
上面代码多为赋值代码,不需要多讲。我们重点关注 doOpen 抽象方法,该方法需要子类实现。下面回到 NettyServer 中。
protected void doOpen() throws Throwable {
// 构建netty server bootstrap
bootstrap = new ServerBootstrap();
// 创建boss线程池
bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
// 创建worker线程池
workerGroup = NettyEventLoopFactory.eventLoopGroup(
getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
"NettyServerWorker");
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopFactory.serverSocketChannelClass())
.option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation",
SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
}
ch.pipeline()
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
// 绑定到指定的 ip 和端口上
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
以上就是 NettyServer 创建的过程,dubbo 2.7.8默认使用的 NettyServer 是基于 netty 4.x 版本实现的,Dubbo 另外提供了 netty 3.x 版本的 NettyServer,大家可在使用 Dubbo 的过程中按需进行配置。
到此,关于服务导出的过程就分析完了。整个过程比较复杂,大家在分析的过程中耐心一些。并且多写 Demo 进行调试,以便能够更好的理解代码逻辑。
本节内容先到这里,接下来分析服务导出的另一块逻辑 — 服务注册。在此回到export方法看register方法:
服务注册
服务注册操作对于 Dubbo 来说不是必需的,通过服务直连的方式就可以绕过注册中心。但通常我们不会这么做,直连方式不利于服务治理,仅推荐在测试服务时使用。对于 Dubbo 来说,注册中心虽不是必需,但却是必要的。因此,关于注册中心以及服务注册相关逻辑,我们也需要搞懂。
接下来以Nacos 注册中心作为分析目标,其他类型注册中心大家可自行分析。>
private void register(URL registryUrl, URL registeredProviderUrl) {
// 获取 Registry
Registry registry = registryFactory.getRegistry(registryUrl);
// 注册服务
registry.register(registeredProviderUrl);
}
register 方法包含两步操作,第一步是获取注册中心实例,第二步是向注册中心注册服务。接下来分两节内容对这两步操作进行分析。
获取注册中心实例
本节以Nacos注册中心为例进行分析,registryFactory.getRegistry(registryUrl)方法中,registryFactory为RegistryFacotry的自适应扩展类,其执行getRegistry方法时,会通过ExtensionLoader.getExtensionLoader(RegsitryFactory.class).getExtension("nacos")获取到NacosRegistryFactory的包装类RegistryFactoryWrapper,最后在该扩展类中执行RegistryFactoryWrapper的getRegistry方法,我们先来看下RegistryFactoryWrapper方法中的getRegistry方法:
public Registry getRegistry(URL url) {
return new ListenerRegistryWrapper(registryFactory.getRegistry(url),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(RegistryServiceListener.class)
.getActivateExtension(url, "registry.listeners")));
}
这里执行逻辑有:>
- 通过registryFactory获取Registry实例
- 通过Dubbo SPI获取RegistryServiceListener
- 返回Registry的包装类ListenerRegistryWrapper
首先来分析下registryFactory获取Registry实例,这里的registryFacotry为NacosRegistryFacotry,NacosRegistryFactory为AbstractRegistryFacotry的子类,其getRegistry方法在其父类AbstractRegistryFacotry中,接下来分析下该方法:
public Registry getRegistry(URL url) {
if (destroyed.get()) {
LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " +
"Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of.");
return DEFAULT_NOP_REGISTRY;
}
url = URLBuilder.from(url)
.setPath(RegistryService.class.getName())
.addParameter(INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(EXPORT_KEY, REFER_KEY)
.build();
String key = createRegistryCacheKey(url);
// Lock the registry access process to ensure a single instance of the registry
LOCK.lock();
try {
// 访问缓存
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
// 缓存未命中,创建 Registry 实例
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
// 写入缓存
REGISTRIES.put(key, registry);
return registry;
} finally {
// Release the lock
LOCK.unlock();
}
}
getRegistry 方法先访问缓存,缓存未命中则调用 createRegistry 创建 Registry,然后写入缓存。这里的 createRegistry 是一个模板方法,由具体的子类实现。因此,下面我们回到 NacosRegistryFactory 中探究一番。
@Override
protected Registry createRegistry(URL url) {
return new NacosRegistry(url, createNamingService(url));
}
继续看createNamingService方法
public static NamingService createNamingService(URL connectionURL) {
// 获取nacos配置
Properties nacosProperties = buildNacosProperties(connectionURL);
NamingService namingService;
try {
// 创建NamingService
namingService = NacosFactory.createNamingService(nacosProperties);
} catch (NacosException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getErrMsg(), e);
}
throw new IllegalStateException(e);
}
return namingService;
}
这个方法逻辑也不多,首先根据URL获取nacos配置,再通过NacosFactory创建NamingService并返回。到这里,就通过NacosRegistryFacotry获取到了Registry实例。在回到这里,第一步逻辑分析完了,第二步是通过Dubbo SPI获取扩展类,这里不再分析,我们在Dubbo SPI章节中有详细分析过,最后是通过第一步和第二步获取到的实例构建Registry的包装类ListenerRegistryWrapper并返回。到这里,获取注册中心实例接分析完毕了。
注册服务
回到RegistryProtocol的register方法,接着看registry.register(registeredProviderUrl)这个步骤,上一步获取到的注册中心实例为Registry的包装类ListenerRegistryWrapper,接下来我们看下该包装类的register方法:
public void register(URL url) {
try {
// 注册 NacosRegistry
registry.register(url);
} finally {
if (CollectionUtils.isNotEmpty(listeners)) {
RuntimeException exception = null;
for (RegistryServiceListener listener : listeners) {
if (listener != null) {
try {
// 注册监听
listener.onRegister(url);
} catch (RuntimeException t) {
logger.error(t.getMessage(), t);
exception = t;
}
}
}
if (exception != null) {
throw exception;
}
}
}
}
这里的逻辑只有两个,通过NacosRegistry进行注册,注册结束后监听该注册,再接着分析NacosRegistry的register方法,这个方法定义在 FailbackRegistry 抽象类中。代码如下:
public void register(URL url) {
if (!acceptable(url)) {
logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type.");
return;
}
// 这里主要是添加缓存
super.register(url);
// 清除缓存
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// 向服务器端发送注册请求
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// 如果启动检测被打开,则直接抛出Exception.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& !CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
// 将失败的注册请求记录到失败列表中,定期重试
addFailedRegistered(url);
}
}
我们重点关注 doRegister 方法调用即可,其他的代码先忽略。doRegister 方法是一个模板方法,因此我们到 FailbackRegistry 子类 NacosRegistry 中进行分析。如下:
public void doRegister(URL url) {
// 获取服务名称
final String serviceName = getServiceName(url);
// 构建nacos实例,主要是一些赋值操作
final Instance instance = createInstance(url);
// 执行注册
execute(namingService -> namingService.registerInstance(serviceName,
getUrl().getParameter(GROUP_KEY, Constants.DEFAULT_GROUP), instance));
}
到这里服务注册就执行完毕了。