Dubbo 3.2版本分析Provider启动前的前菜

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

  1. 关于 **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
  2. 这里是设置 **application,**对应的 config 类型很明显是 org.apache.dubbo.config.ApplicationConfig。
  3. 然后是对 **scopeModel 参数的判断,**官网对这个参数又叫做领域模型。

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

  • 让Dubbo支持多应用的部署,这块一些大企业有诉求
  • 从架构设计上,解决静态属性资源共享、清理的问题
  • 分层模型将应用的管理和服务的管理分开
  1. 执行**configsCache.computeIfAbsent,configsCache **类型为 Map<String, Map<String, AbstractConfig>>,为成员变量。调用 computeIfAbsent方法的时候,会判断key是否存在,存在则返回对应value,反之,先初始化value。
  2. 判断是否为消费端引用配置类型或者为服务端服务配置类型(这两种配置为我们应用服务相关的配置,需要保证动态更新)。如果不是上面两种配置,会从 configsMap 获取是否对应的配置类型,有则返回,因为这里我们是第一次启动服务,configsMap 的value为空的,所以为继续往下走。
  3. 最后进入 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

  1. 和设置 application异曲同工之妙,这里同样先设置领域模型,负责管理我们的配置中心,接着往我们的 **configManager **添加注册中心。
  2. addRegistry方法里,调用的还是我们上面代码的 addConfig 方法,只不过这次这里参数 config 的配置类型是org.apache.dubbo.config.RegistryConfig。
  3. 由于 configsMap 没有存在对应的注册信息,最终会进入 synchronized区域,往我们的 configsCache添加注册信息,如图1所示。
public DubboBootstrap registry(RegistryConfig registryConfig) {
        registryConfig.setScopeModel(applicationModel);
        configManager.addRegistry(registryConfig);
        return this;
    }

图1
图1

设置 protocol

  1. 这里我们采用的是 dubbo 协议,在服务启动时指定:new ProtocolConfig(CommonConstants.DUBBO, -1)。
  2. 同样先设置领域模型,然后添加协议配置信息。配置类型为 **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图2

设置 serviceConfig

  1. 不同于前面的注册中心等公共配置的操作,serviceConfig 的添加通过 getConfigManager() 方法返回的时ModuleConfigManager 类的实例
  2. 前面的注册中心等公共配置的添加,是通过 ConfigManager类的实例去操作,关于 ConfigManager 和ModuleConfigManager,我们可以看图3的类结构图。
  3. 兜兜转转,还是来到 AbstractConfigManageraddConfig方法。关于它的实现已经在上面展示。这里传入的 config 类型是 org.apache.dubbo.config.ServiceConfig。
  4. 如图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
图3

图4
图4

小结

本文对 Dubbo 3.2 源码 provider 启动前进行的操作做了大概的梳理,可以知道大体流程还是遵循原来的设计,设置 application -> 设置 registry -> 设置 protocol -> 设置 serviceConfig 。但在具体实现上跟原来版本也有所区别,比如引入了领域模型进行分层次管理。下一篇文章,我们将分析 Dubbo 3.2 源码 provider 具体的启动流程。