前面已经解读过Dubbo SPI相关的源码见:一篇短文就能搞定Dubbo SPI 源码及示例。本文主要研究一下 Dubbo 导出服务的过程。Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑。整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装 URL。第二部分是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。第三部分是向注册中心注册服务,用于服务发现。本文将基于dubbo-2.7.7
源码对这三个部分代码进行详细的分析。
在2.7.5之前服务导出的入口方法是 ServiceBean 的 onApplicationEvent。onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务导出操作。但是在2.7.5开始新增了DubboBootstrapApplicationListener,该类继承了OneTimeExecutionApplicationContextEventListener抽象类,OneTimeExecutionApplicationContextEventListener又实现了ApplicationListener接口onApplicationEvent方法,同之前版本一样,该方法会在收到 Spring 上下文刷新事件后执行。两个类的关键源码如下:
abstract class OneTimeExecutionApplicationContextEventListener implements ApplicationListener, ApplicationContextAware {
private ApplicationContext applicationContext;
public final void onApplicationEvent(ApplicationEvent event) {
//判断事件源是持有的ApplicationContext并且是应用上下文事件
if (isOriginalEventSource(event) && event instanceof ApplicationContextEvent) {
onApplicationContextEvent((ApplicationContextEvent) event);
}
}
/**
* The subclass overrides this method to handle {@link ApplicationContextEvent}
*
* @param event {@link ApplicationContextEvent}
*/
protected abstract void onApplicationContextEvent(ApplicationContextEvent event);
//省略部分源码
}
public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
implements Ordered {
/**
* The bean name of {@link DubboBootstrapApplicationListener}
*/
public static final String BEAN_NAME = "dubboBootstrapApplicationListener";
private final DubboBootstrap dubboBootstrap;
public DubboBootstrapApplicationListener() {
this.dubboBootstrap = DubboBootstrap.getInstance();
}
@Override
public void onApplicationContextEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {//是上下文刷新事件则调用DubboBootstrap的start方法
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {//如果是上下文关闭事件则调用DubboBootstrap的stop方法
onContextClosedEvent((ContextClosedEvent) event);
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
private void onContextClosedEvent(ContextClosedEvent event) {
dubboBootstrap.stop();
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
根据上面的源码可知,在DubboBootstrapApplicationListener 的构造函数中会先去获取DubboBootstrap 的实例,在监听到ContextRefreshedEvent事件时触发DubboBootstrap 的start方法,因此接着我们看看DubboBootstrap 的实例化及开始方法的源码。
DubboBootstrap 实例化
public class DubboBootstrap extends GenericEventListener {
private static DubboBootstrap instance;
private final ConfigManager configManager;
private final Environment environment;
/**
* 加锁构造单例
* See {@link ApplicationModel} and {@link ExtensionLoader} for why DubboBootstrap is designed to be singleton.
*/
public static synchronized DubboBootstrap getInstance() {
if (instance == null) {
instance = new DubboBootstrap();
}
return instance;
}
//私有化构造函数,保证只能被自己实例化
private DubboBootstrap() {
//通过SPI方式获取环境配置和环境管理实例,继承关系如下图
configManager = ApplicationModel.getConfigManager();
environment = ApplicationModel.getEnvironment();
//注册shutdown事件,回调DubboBootstrap的destroy方法,销毁所有导出及引用的服务等
DubboShutdownHook.getDubboShutdownHook().register();
ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() {
@Override
public void callback() throws Throwable {
DubboBootstrap.this.destroy();
}
});
}
org.apache.dubbo.config.bootstrap.DubboBootstrap.start()
/**
* Start the bootstrap
*/
public DubboBootstrap start() {
//原子操作,保证只启动一次
if (started.compareAndSet(false, true)) {
//初始化操作
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 导出dubbo服务(最终调用ServiceConfig的export方法)
exportServices();
// 不仅仅是注册服务提供者或者已经导出元数据
if (!isOnlyRegisterProvider() || hasExportedServices()) {
// 导出元数据服务(最终构建了ServiceConfig实例,然后调用export方法)
exportMetadataService();
// 如果有则需要注册本地服务实例,通过SPI方式获取服务发现注册中心,然后调用他们的注册方法(默认自适应拓展实现是zookeeper)
registerServiceInstance();
}
//执行服务引入
referServices();
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
根据上面start方法源码可以看到,里面一次执行了服务初始化以服务导出和引入的方法,接下来我们追踪下个方法的具体实现。
org.apache.dubbo.config.bootstrap.DubboBootstrap.initialize()
/**
* Initialize
*/
private void initialize() {
// 原子操作确保只初始化一次
if (!initialized.compareAndSet(false, true)) {
return;
}
//初始化Dubbo组件的生命周期,这里主要是对环境配置初始化
ApplicationModel.iniFrameworkExts();
//开始构建配置中心
startConfigCenter();
//如果是zookeeper作为注册中心且没有指定配置中心时,使用注册中心做配置中心
useRegistryAsConfigCenterIfNecessary();
//加载协议ID到协议配置中并加载注册id到注册中心配置
loadRemoteConfigs();
//全局配置校验(应用、元数据、提供者、消费者、监控等)
checkGlobalConfigs();
//初始化元数据服务
initMetadataService();
//初始化事件监听器(将当前实例添加到事件监听器中)
initEventListener();
if (logger.isInfoEnabled()) {
logger.info(NAME + " has been initialized!");
}
}
org.apache.dubbo.config.bootstrap.DubboBootstrap.exportServices()
private void exportServices() {
configManager.getServices().forEach(sc -> {
//设置ServiceConfig的启动器为当前实例
ServiceConfig serviceConfig = (ServiceConfig) sc;
serviceConfig.setBootstrap(this);
if (exportAsync) {
//异步导出,将导出任务提交到线程池异步完成
ExecutorService executor = executorRepository.getServiceExporterExecutor();
Future<?> future = executor.submit(() -> {
sc.export();
});
asyncExportingFutures.add(future);
} else {
//同步导出 则直接调用ServiceConfig的导出方法
sc.export();
exportedServices.add(sc);
}
});
}
org.apache.dubbo.config.bootstrap.DubboBootstrap.registerServiceInstance()
private void registerServiceInstance() {
//判断是否有ServiceDiscoveryRegistry,有则通过他获取ServiceDiscovery放入集合中
if (CollectionUtils.isEmpty(getServiceDiscoveries())) {
//没有则直接返回
return;
}
// 获取应用配置
ApplicationConfig application = getApplication();
//从配置中获取服务名称
String serviceName = application.getName();
//获取元数据导出地址
URL exportedURL = selectMetadataServiceExportedURL();
//获取主机
String host = exportedURL.getHost();
//获取端口
int port = exportedURL.getPort();
//根据名称主机和端口创建DefaultServiceInstance实例
ServiceInstance serviceInstance = createServiceInstance(serviceName, host, port);
//获取服务发现中心调用注册方法,注册服务实例
getServiceDiscoveries().forEach(serviceDiscovery -> serviceDiscovery.register(serviceInstance));
}
根据上面源码分析可知,导出服务方法最终都会调用ServiceConfig的export方法进行导出,接下来将进入这个方法源码分析。
public synchronized void export() {
//判断当前服务是否需要导出
if (!shouldExport()) {
return;
}
//启动类为空则获取一个实例
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.init();
}
//校验并更细配置(默认配置、协议配置等)
checkAndUpdateSubConfigs();
//初始化元数据(设置版本、分组、类型及名称等属性)
serviceMetadata.setVersion(version);
serviceMetadata.setGroup(group);
serviceMetadata.setDefaultGroup(group);
serviceMetadata.setServiceType(getInterfaceClass());
serviceMetadata.setServiceInterfaceName(getInterface());
serviceMetadata.setTarget(getRef());
//是否需要延迟导出
if (shouldDelay()) {
//提交导出任务到延迟导出调度器(可调度线程池)
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
//执行导出操作
doExport();
}
//执行已导出操作
exported();
}
public void exported() {
// 发布一个服务导出事件( ServiceConfigExportedEvent)
dispatch(new ServiceConfigExportedEvent(this));
}
上一步导出源码可知,除了一些初始化及延迟导出的校验外,还有两个主要的逻辑:在执行导出前的配置校验更新操作;执行导出逻辑。本节首先分析配置校验部分的源码,然后分析执行导出源码。
org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs()
private void checkAndUpdateSubConfigs() {
// 完成组件注册(协议、注册中心)
completeCompoundConfigs();
//检查provider是否为空,为空则获取默认默认的provider,默认的不存在则新建一个ProviderConfig实例
checkDefault();
//检查协议,如果协议为空并且provider不为空,则获取provider中的协议;将根据协议ID转换协议
checkProtocol();
// 初始化一些为空的配置
List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
.getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
configInitializers.forEach(e -> e.initServiceConfig(this));
// 如果协议不是仅仅是injvm即需要导出服务给外部使用,则校验注册中心
if (!isOnlyInJvm()) {
//检验注册中心配置是否存在,然后通过id转换成注册中心配置RegistryConfig
// 转换完成后遍历所有Registry是否可用(注册地址是否为空),不可用则会抛出异常
checkRegistry();
}
//调用刷新配置方法
//1. 首先调用org.apache.dubbo.common.config.Environment.getPrefixedConfiguration(AbstractConfig)方法,
//该方法会从多种配置源(AbstractConfig (API, XML, annotation), - D, config center)中找出Application, Registry, Protocol等的最优配置
//2.通过getClass().getMethods()获取所有的方法,判断是否是setter或者setParameters方法,如果是就通过反射调用将将新的配置设置进去
this.refresh();
//接口名称为空,抛出异常
if (StringUtils.isEmpty(interfaceName)) {
throw new IllegalStateException(" interface not allow null!");
}
//检查引用实现是否是泛化服务类型
if (ref instanceof GenericService) {
//设置接口类为泛化服务类
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
//泛化服务标识符为空则设置为true
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());
//检查引用不应为空,并且是给定接口的实现
checkRef();
generic = Boolean.FALSE.toString();
}
//服务接口的本地实现类名不为空
if (local != null) {
//如果local不是类名而是true,则拼接类名
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);
}
}
//本地存根和上面的local逻辑一致
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);
}
}
//本地存根合法性校验,主要也是校验了是否是之指定接口的实现
checkStubAndLocal(interfaceClass);
//Mock校验,主要校验值mock值是否合法及mock类是否合法(实现指定接口并且有默认构造函数)
ConfigValidationUtils.checkMock(interfaceClass, this);
//服务配置校验(版本路径票据、扩展校验(ExporterListener、ProxyFactory、InvokerListener、Cluster等)、注册中心、协议即提供者校验)
ConfigValidationUtils.validateServiceConfig(this);
//配置后置处理器调用ConfigPostProcessor#postProcessServiceConfig方法
postProcessConfig();
}
org.apache.dubbo.config.ServiceConfig.doExport()
protected synchronized void doExport() {
//是否执行了unexport方法(表示服务注销了),执行了则抛出异常
if (unexported) {
throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
}
//已经导出过则直接返回
if (exported) {
return;
}
//设置已导出标志
exported = true;
//路径及服务名为空
if (StringUtils.isEmpty(path)) {
//服务名为接口名
path = interfaceName;
}
//执行多协议多注册导出
doExportUrls();
}
根据上一步中的导出逻辑可知,最后调用了doExportUrls方法,该方法对dubbo多协议,多注册中心进行了支持,源码如分析如下:
private void doExportUrls() {
//获取服务缓存库
ServiceRepository repository = ApplicationModel.getServiceRepository();
//注册当前服务到本地缓存库
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
//注册服务提供者到缓存库
repository.registerProvider(
//根据接口名,服务组及版本号生成唯一名称
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
//加载注册中心地址(支持多注册中心,因此是集合)
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
for (ProtocolConfig protocolConfig : protocols) {//支持多协议导出
//根据协议配置生成服务地址(注册中心的key)
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
// 如果指定了服务路径,需要再次注册到缓存中,保证此映射路径能获取到服务
repository.registerService(pathKey, interfaceClass);
// 设置原数据服务key
serviceMetadata.setServiceKey(pathKey);
//执行多中心单协议的导出逻辑
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
通过上面的源码分析可知,多协议导出首次调用ConfigValidationUtils#loadRegistries方法加载多注册中心,然后遍历每个协议执行该协议的导出逻辑。
public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
// 获取服务应用配置
ApplicationConfig application = interfaceConfig.getApplication();
// 获取服务注册配置
List<RegistryConfig> registries = interfaceConfig.getRegistries();
if (CollectionUtils.isNotEmpty(registries)) {// 注册配置不为空
for (RegistryConfig config : registries) {// 遍历注册配置
// 获取注册地址
String address = config.getAddress();
if (StringUtils.isEmpty(address)) {
// 如果为设置默认值0.0.0.0
address = ANYHOST_VALUE;
}
// 如果地址不是N/A
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
// 通过getter或者getParameters方法将ApplicationConfig和RegistryConfig属性放到map中
AbstractConfig.appendParameters(map, application);
AbstractConfig.appendParameters(map, config);
// 存入map
map.put(PATH_KEY, RegistryService.class.getName());
// 添加dubbo、版本、时间戳等参数到map中
AbstractInterfaceConfig.appendRuntimeParameters(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) {
// 根据添加registry参数组装注册中心地址
url = URLBuilder.from(url).addParameter(REGISTRY_KEY, url.getProtocol())
.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;
}
有多协议多注册中心源码分析可知,最终调用了但协议多注册中心导出方法,本方法的源码比较长,主要逻辑就是导出前先获取了各种配置缓存起来,然后获取创建包装实例、获取主机和端口,最后执行导出。导出根据导出范围分为三个分支:scope = none,不导出服务;scope != remote,导出到本地;scope != local,导出到远程。
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);
// 添加dubbo、版本、时间戳等运行参数到map中
ServiceConfig.appendRuntimeParameters(map);
// 通过getter或者getParameters方法将ApplicationConfig、ModuleConfig、MetricsConfig、
// ProviderConfig、ProtocolConfig及ServiceConfig属性放到map中
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, provider);
AbstractConfig.appendParameters(map, protocolConfig);
AbstractConfig.appendParameters(map, this);
// 获取原数导出配置,如果合法则添加到map中
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
//是否存在 标签的配置信息
if (CollectionUtils.isNotEmpty(getMethods())) {
for (MethodConfig method : getMethods()) {
// 通过getter或者getParameters方法将MethodConfig属性添加到map中,前缀是当前MethodConfig名称即<方法名.属性名,属性值>。
AbstractConfig.appendParameters(map, method, method.getName());
String retryKey = method.getName() + ".retry";
//是否存在methodname.retry键,及是否配置了retry属性
if (map.containsKey(retryKey)) {
//存在则移除
String retryValue = map.remove(retryKey);
//如果retry配置的false,设置retries配置为0
if ("false".equals(retryValue)) {
map.put(method.getName() + ".retries", "0");
}
}
//获取方法参数配置ArgumentConfig,存放到map中
List<ArgumentConfig> arguments = method.getArguments();
if (CollectionUtils.isNotEmpty(arguments)) {
for (ArgumentConfig argument : arguments) {
// 判断参数类型是否为空
if (argument.getType() != null && argument.getType().length() > 0) {
//获取接口(导出服务)的所有方法,遍历
Method[] methods = interfaceClass.getMethods();
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();
// 参数索引不是-1,-1表示未设置
if (argument.getIndex() != -1) {
// 检测 ArgumentConfig 中的 type 属性与方法参数列表中的参数名称是否一致
if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
//一致的则添加配置到map<方法名.参数索引,参数配置>
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 {//未设置参数索引
// 遍历方法参数的所有类型
for (int j = 0; j < argtypes.length; j++) {
Class<?> argclazz = argtypes[j];
//查找和当前参数配置类型匹配的参数
if (argclazz.getName().equals(argument.getType())) {
//匹配则将配置放入map中<方法名.参数索引,参数配置>
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());
}
}
}
}
}
}
}
} else if (argument.getIndex() != -1) {
//参数类型为空但是参数索引不是-1,添加配置到map中
AbstractConfig.appendParameters(map, argument,
method.getName() + "." + argument.getIndex());
} else {
//如果既没有配置参数索引又没哟配置参数类型则抛出异常
throw new IllegalArgumentException(
"Argument config must set index or type attribute.eg: or ");
}
}
}
}
}
if (ProtocolUtils.isGeneric(generic)) {
//是泛化服务则设置、表示任意方法
map.put(GENERIC_KEY, generic);
map.put(METHODS_KEY, ANY_VALUE);
} else {
//不是泛化服务,获取修订版本号,放入map
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
//创建并返回包装类方法名称
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
//没有包装方法名则设置
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
//如果有,则方面通过逗号分隔拼接放入map中,key=methods
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
//没有token且提供者不为空,获取提供者的token
if (ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
//如果token不为空
if (!ConfigUtils.isEmpty(token)) {
//如果token是默认值(true或者default),则创建UUID作为token
if (ConfigUtils.isDefault(token)) {
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}
// init serviceMetadata attachments
serviceMetadata.getAttachments().putAll(map);
// 找到配置的主机,优先级如下:environment variables -> java system properties -> host property in config file
// -> /etc/hosts -> default network address -> first available network address
String host = findConfigedHosts(protocolConfig, registryURLs, map);
// 找到配置的端口,优先级: environment variable -> java system properties ->
// port property in protocol config file -> protocol default port
Integer port = findConfigedPorts(protocolConfig, name, map);
//拼接URL
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
// 如果存在则获取自定义扩展配置
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);
// 如果导出单位不是none(none则不执行导出)
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 如果导出范围不是remote则执行本地导出逻辑
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 如果配置导出范围不是local则执行远程导出
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
//注册中心地址不为空
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
// 如果协议是injvm 则不执行导出注册逻辑
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
//url配置dynamic参数
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
//获取监控地址
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
//监控地址不为空则添加到URL参数中key=monitor
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);
}
}
// 获取自定义代理配置
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
//如果存在,则为注册地址添加代理实现参数
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
//为服务引用生成Invoker对象
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
//生成提供者和配置包装Invoker
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker,
this);
//通过SPI自适应拓展获取Protocol的拓展实现,调用导出方法
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);
}
}
}
//添加到服务引用url缓存中
this.urls.add(url);
}
关于服务导出注册源码太多,因此分了两次解析,后半部分请参考下篇博客:超详细Dubbo服务导出源码解读(二)