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 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.ApplicationConfigorg.apache.dubbo.config.MonitorConfigorg.apache.dubbo.config.MetricsConfigorg.apache.dubbo.config.SslConfigorg.apache.dubbo.config.ProtocolConfigorg.apache.dubbo.config.RegistryConfigclass org.apache.dubbo.config.ConfigCenterConfigorg.apache.dubbo.config.MetadataReportConfigorg.apache.dubbo.config.TracingConfig 这里是设置 **application,**对应的 config 类型很明显是 org.apache.dubbo.config.ApplicationConfig。然后是对 **scopeModel 参数的判断,**官网对这个参数又叫做领域模型。

根据官网的文档描述,Dubbo3中引入“模型的概念”来进行服务级别的管理工作,像配置中心和元数据中心的管理。而这么做的原因,也有着对应的解释:

让Dubbo支持多应用的部署,这块一些大企业有诉求从架构设计上,解决静态属性资源共享、清理的问题分层模型将应用的管理和服务的管理分开

执行**configsCache.computeIfAbsent,configsCache **类型为 Map>,为成员变量。调用 computeIfAbsent方法的时候,会判断key是否存在,存在则返回对应value,反之,先初始化value。判断是否为消费端引用配置类型或者为服务端服务配置类型(这两种配置为我们应用服务相关的配置,需要保证动态更新)。如果不是上面两种配置,会从 configsMap 获取是否对应的配置类型,有则返回,因为这里我们是第一次启动服务,configsMap 的value为空的,所以为继续往下走。最后进入 synchronized 锁区域,这里执行 addIfAbsent 方法,会把我们传入的config放到 configsCache中,注意!这里即使存在相同的key,也会进行覆盖更新。

public final 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 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 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 具体的启动流程。

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: