Dubbo 3.2版本分析Provider启动前的前菜
- 写在前面
- 例子
- 分析
-
- 设置 application
- 设置 registry
- 设置 protocol
- 设置 serviceConfig
- 小结
写在前面
一直以来对Dubbo的实现原理颇为好奇,前几个月在Dubbo专栏里也陆续写了几遍文章,奈何自身原因+客观原因,就没有坚持下来。这次重启Dubbo源码刨析系列,也刚好可以借着2023年7月发布的比较新的Dubbo 3.2版本源码进行学习研究,共勉!
例子
这里我们还是以provider启动的Demo为入口,进行分析:
- 如下,为Dubbo服务暴漏出来的接口:
public interface DemoService { String sayHello(String name); }
- DemoServiceImpl为Dubbo provider
public class DemoServiceImpl implements DemoService { private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); @Override public String sayHello(String name) { logger.info("Hello " + name + ", request from consumer: " + RpcContext.getServiceContext().getRemoteAddress()); return "Hello " + name + ", response from provider: " + RpcContext.getServiceContext().getLocalAddress(); } }
- Application为服务的启动类
public class Application { private static final String REGISTRY_URL = "zookeeper://sr-1-zk-cluster-1.gz.cvte.cn:2181"; public static void main(String[] args) { startWithBootstrap(); } private static void startWithBootstrap() { ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); DubboBootstrap bootstrap = DubboBootstrap.getInstance(); bootstrap .application(new ApplicationConfig("dubbo-api-provider")) .registry(new RegistryConfig(REGISTRY_URL)) .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) .service(service) .start() .await(); } }
分析
在服务正式启动前(**start **方法),需要设置上下文 **application ,**接着配置注册中心地址信息 **registry ,**最后设置我们的 provider service 服务信息。
bootstrap .application(new ApplicationConfig("dubbo-api-provider")) .registry(new RegistryConfig(REGISTRY_URL)) .protocol(new ProtocolConfig(CommonConstants.DUBBO, -1)) .service(service) .start()
设置 application
- 关于 **application **方法,通过 debug 我们可以发现如下的一段的代码,这里有一个关于支持的配置类型的判断方法:isSupportConfigType,可以发现目前支持的配置有如下9种。
- org.apache.dubbo.config.ApplicationConfig
- org.apache.dubbo.config.MonitorConfig
- org.apache.dubbo.config.MetricsConfig
- org.apache.dubbo.config.SslConfig
- org.apache.dubbo.config.ProtocolConfig
- org.apache.dubbo.config.RegistryConfig
- class org.apache.dubbo.config.ConfigCenterConfig
- org.apache.dubbo.config.MetadataReportConfig
- org.apache.dubbo.config.TracingConfig
- 这里是设置 **application,**对应的 config 类型很明显是 org.apache.dubbo.config.ApplicationConfig。
- 然后是对 **scopeModel 参数的判断,**官网对这个参数又叫做领域模型。
根据官网的文档描述,Dubbo3中引入“模型的概念”来进行服务级别的管理工作,像配置中心和元数据中心的管理。而这么做的原因,也有着对应的解释:
- 让Dubbo支持多应用的部署,这块一些大企业有诉求
- 从架构设计上,解决静态属性资源共享、清理的问题
- 分层模型将应用的管理和服务的管理分开
- 执行**configsCache.computeIfAbsent,configsCache **类型为 Map<String, Map<String, AbstractConfig>>,为成员变量。调用 computeIfAbsent方法的时候,会判断key是否存在,存在则返回对应value,反之,先初始化value。
- 判断是否为消费端引用配置类型或者为服务端服务配置类型(这两种配置为我们应用服务相关的配置,需要保证动态更新)。如果不是上面两种配置,会从 configsMap 获取是否对应的配置类型,有则返回,因为这里我们是第一次启动服务,configsMap 的value为空的,所以为继续往下走。
- 最后进入 synchronized 锁区域,这里执行 addIfAbsent 方法,会把我们传入的config放到 configsCache中,注意!这里即使存在相同的key,也会进行覆盖更新。
public final <T extends AbstractConfig> T addConfig(AbstractConfig config) { if (config == null) { return null; } // ignore MethodConfig if (!isSupportConfigType(config.getClass())) { throw new IllegalArgumentException("Unsupported config type: " + config); } if (config.getScopeModel() != scopeModel) { config.setScopeModel(scopeModel); } Map<String, AbstractConfig> configsMap = configsCache.computeIfAbsent(getTagName(config.getClass()), type -> new ConcurrentHashMap<>()); // fast check duplicated equivalent config before write lock if (!(config instanceof ReferenceConfigBase || config instanceof ServiceConfigBase)) { for (AbstractConfig value : configsMap.values()) { if (value.equals(config)) { return (T) value; } } } // lock by config type synchronized (configsMap) { return (T) addIfAbsent(config, configsMap); } }
设置 registry
- 和设置 application异曲同工之妙,这里同样先设置领域模型,负责管理我们的配置中心,接着往我们的 **configManager **添加注册中心。
- addRegistry方法里,调用的还是我们上面代码的 addConfig 方法,只不过这次这里参数 config 的配置类型是org.apache.dubbo.config.RegistryConfig。
- 由于 configsMap 没有存在对应的注册信息,最终会进入 synchronized区域,往我们的 configsCache添加注册信息,如图1所示。
public DubboBootstrap registry(RegistryConfig registryConfig) { registryConfig.setScopeModel(applicationModel); configManager.addRegistry(registryConfig); return this; }
图1
设置 protocol
- 这里我们采用的是 dubbo 协议,在服务启动时指定:new ProtocolConfig(CommonConstants.DUBBO, -1)。
- 同样先设置领域模型,然后添加协议配置信息。配置类型为 **org.apache.dubbo.config.ProtocolConfig **然后执行 **addProtocol **方法,本质还是执行 **addConfig 方法。最终添加配置后, configsCache **信息,如图2所示。
public DubboBootstrap protocols(List<ProtocolConfig> protocolConfigs) { if (CollectionUtils.isEmpty(protocolConfigs)) { return this; } for (ProtocolConfig protocolConfig : protocolConfigs) { protocolConfig.setScopeModel(applicationModel); configManager.addProtocol(protocolConfig); } return this; }
图2
设置 serviceConfig
- 不同于前面的注册中心等公共配置的操作,serviceConfig 的添加通过 getConfigManager() 方法返回的时ModuleConfigManager 类的实例。
- 前面的注册中心等公共配置的添加,是通过 ConfigManager类的实例去操作,关于 ConfigManager 和ModuleConfigManager,我们可以看图3的类结构图。
- 兜兜转转,还是来到 AbstractConfigManager 的 addConfig方法。关于它的实现已经在上面展示。这里传入的 config 类型是 org.apache.dubbo.config.ServiceConfig。
- 如图4,最终还是会通过 configCache创建 key 为 “service”,value 为 serviceConfig 的实例。注意,这里的 configCache 为 ModuleConfigManager 的成员变量。而前面的注册中心等公共配置的操作,最终添加到的 configCache 为 ConfigManager 类的成员变量。
public DubboBootstrap service(ServiceConfig<?> serviceConfig) { this.service(serviceConfig, applicationModel.getDefaultModule()); return this; } public DubboBootstrap service(ServiceConfig<?> serviceConfig, ModuleModel moduleModel) { serviceConfig.setScopeModel(moduleModel); moduleModel.getConfigManager().addService(serviceConfig); return this; } public void addService(ServiceConfigBase<?> serviceConfig) { addConfig(serviceConfig); }
图3
图4
小结
本文对 Dubbo 3.2 源码 provider 启动前进行的操作做了大概的梳理,可以知道大体流程还是遵循原来的设计,设置 application -> 设置 registry -> 设置 protocol -> 设置 serviceConfig 。但在具体实现上跟原来版本也有所区别,比如引入了领域模型进行分层次管理。下一篇文章,我们将分析 Dubbo 3.2 源码 provider 具体的启动流程。