Dubbo的启动过程源码分析 -- 初始化过程(4)

Dubbo的启动过程源码分析 – 初始化过程

因为我也很多不明白的地方,所以这篇文章中很多没有说明白的地方。 我会在后面的文章中逐步弄明白的。

目前我们项目中使用Dubbo的方式都是整合Spring一起使用,在Spring启动之后再启动Dubbo. 关于dubbo整合Spring的源码我前面已经写了几篇文章。 本次为了更好的说明白dubbo的启动原理,会剥离Spring,单独的讲解dubbo的启动过程。

Dubbo源码中提供了单独启动Dubbo的demo, 我就从这里开始

image-20200707111358183

启动类

public class Application {
    public static void main(String[] args) throws Exception {
        if (isClassic(args)) {
            startWithExport();
        } else {
            startWithBootstrap();
        }
    }

    private static boolean isClassic(String[] args) {
        return args.length > 0 && "classic".equalsIgnoreCase(args[0]);
    }
		//新的启动方法, 主要讲解这里启动方式的源码
    private static void startWithBootstrap() {
        //封装服务提供者, ServiceConfig大家应该很熟悉
        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());
      	//dubbo的一些全局配置
        ApplicationConfig applicationConfig = new ApplicationConfig("dubbo-demo-api-provider");
        DubboBootstrap bootstrap = DubboBootstrap.getInstance();
        //设置启动的一些配置
        bootstrap.application(applicationConfig)
                .registry(new RegistryConfig("zookeeper://www.xiezc.xyz:8181"))
                .service(service)
                .start()
                .await();
    }
		//兼容以前的启动方法,最新的已经不推荐使用这种方式
    private static void startWithExport() throws InterruptedException {
        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());
        service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
        service.setRegistry(new RegistryConfig("zookeeper://www.xiezc.xyz:8181"));
        service.export();

        System.out.println("dubbo service started");
        new CountDownLatch(1).await();
    }
}

startWithBootstrap

主要启动方法,方法内部先封装了服务启动者信息ServiceConfig, 在配置了应用信息ApplicationConfig, 最后将这些信息前部注册进DubboBootstrap中, DubboBootstrap是dubbo最新的启动类。 我们可以看下

bootstrap.application(applicationConfig)
                .registry(new RegistryConfig("zookeeper://www.xiezc.xyz:8181"))
                .service(service)
                .start()
                .await();

registry方法就是将注册中心信息放入configManager中

public DubboBootstrap registry(RegistryConfig registryConfig) {
    configManager.addRegistry(registryConfig);
    return this;
}

service方法也是提供者信息放入configManager

public DubboBootstrap service(ServiceConfig<?> serviceConfig) {
    configManager.addService(serviceConfig);
    return this;
}

DubboBootstrap中还有很多的方法都是将信息放入configManager中, 从他的名字知道这个应该是专门管理dubbo的配置信息的类。ConfigManager类在后面会讲解。

start 启动方法

public DubboBootstrap start() {
    if (started.compareAndSet(false, true)) {
        ready.set(false);
        //初始化一些信息等
        initialize();
        if (logger.isInfoEnabled()) {
            logger.info(NAME + " is starting...");
        }
        // 1. export Dubbo Services
        //暴露dubbo服务提供者
        exportServices();

        // Not only provider register
        if (!isOnlyRegisterProvider() || hasExportedServices()) {
            // 2. export MetadataService
            exportMetadataService();
            //3. Register the local ServiceInstance if required
            registerServiceInstance();
        }
        //订阅服务
        referServices();
        if (asyncExportingFutures.size() > 0) {
            new Thread(() -> {
                try {
                    this.awaitFinish();
                } catch (Exception e) {
                    logger.warn(NAME + " exportAsync occurred an exception.");
                }
                ready.set(true);
                if (logger.isInfoEnabled()) {
                    logger.info(NAME + " is ready.");
                }
            }).start();
        } else {
            ready.set(true);
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is ready.");
            }
        }
        if (logger.isInfoEnabled()) {
            logger.info(NAME + " has started.");
        }
    }
    return this;
}

从启动中可以看到十分重要的有四个类:

  • initialize(); 初始化
  • exportServices(); 暴露服务
  • exportMetadataService和registerServiceInstance 注册本地服务
  • referServices 订阅服务

initialize初始化

初始化中会做还多的准备工作, 是必不可少的。

private void initialize() {
    if (!initialized.compareAndSet(false, true)) {
        return;
    }
    //初始化框架的代码,这里是静态类,使用的是SPI的加载方式
    ApplicationModel.initFrameworkExts();

    startConfigCenter();

    useRegistryAsConfigCenterIfNecessary();

    loadRemoteConfigs();

    checkGlobalConfigs();

    initMetadataService();

    initEventListener();

    if (logger.isInfoEnabled()) {
        logger.info(NAME + " has been initialized!");
    }
}
initFrameworkExts初始化FrameworkExt类
//其实在这个静态方法中已经加载了FrameworkExt的类
private static final ExtensionLoader<FrameworkExt> LOADER = ExtensionLoader.getExtensionLoader(FrameworkExt.class);
//这里只是遍历调用下FrameworkExt的initialize方法。
public static void initFrameworkExts() {
    Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
    for (FrameworkExt ext : exts) {
        ext.initialize();
    }
}

可以看到就是使用ExtensionLoader加载FrameworkExt的实现类,

image-20200707203317797

FrameworkExt的实现类从图中可以看到三个, 其中ConfigManager应该很熟悉, 就是前面的提到的。 initFrameworkExts中会分别调用这三个实现类的的initialize方法, 可是从源码看到只有Environment实现了这个方法,其他的都是空实现。

startConfigCenter方法, 开始加载配置中心的配置信息;
private void startConfigCenter() {
    //从配置管理器中获取配置中心的配置, 
    Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();

    // check Config Center
    if (CollectionUtils.isEmpty(configCenters)) {
        //如果没有配置中心,则生成一个默认配置中心放入配置管理器中
        ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
        configCenterConfig.refresh();
        //这一步校验肯定通不过, 因为configCenterConfig是直接new出来的, 没有配置信息
        if (configCenterConfig.isValid()) {
            configManager.addConfigCenter(configCenterConfig);
            configCenters = configManager.getConfigCenters();
        }
    } else {
        //如果有配置中信息的设置,则遍历每个配置中信息,并刷新refresh配置中心
        for (ConfigCenterConfig configCenterConfig : configCenters) {
            configCenterConfig.refresh();
            ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
        }
    }
    //再将每个配置中心放入一个组合的CompositeDynamicConfiguration中,之后CompositeDynamicConfiguration方法放入 environment中
    if (CollectionUtils.isNotEmpty(configCenters)) {
        CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration();
        for (ConfigCenterConfig configCenter : configCenters) {
            compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
        }
        environment.setDynamicConfiguration(compositeDynamicConfiguration);
    }
    configManager.refreshAll();
}

这个方法主要是处理多个配置中心的情况,最后就多个配置中心整合成一个放入配置管理器中。 配置中心的configCenterConfig的 refresh方法会十分重要。

会在后面的Environment类中讲解。

useRegistryAsConfigCenterIfNecessary

从这个方法的名称意思理解,如果需要则把注册中心作为配置中心,我们看下dubbo是如何判断是否需要的

private void useRegistryAsConfigCenterIfNecessary() {
    // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
    //判断上一步 是否已经组合处理好了配置中心, 如果处理好了,就直接返回了。 
    if (environment.getDynamicConfiguration().isPresent()) {
        return;
    }
    //判断配置管理器中是否有配置中心,如果有就直接返回。 
    if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) {
        return;
    }

   // 遍历注册中心, 并根据每个注册中心生成一个配置中心的配置
    configManager.getDefaultRegistries().stream()
            .filter(registryConfig -> registryConfig.getUseAsConfigCenter() == null || registryConfig.getUseAsConfigCenter())
            .forEach(registryConfig -> {
                String protocol = registryConfig.getProtocol();
                String id = "config-center-" + protocol + "-" + registryConfig.getPort();
                ConfigCenterConfig cc = new ConfigCenterConfig();
                cc.setId(id);
                if (cc.getParameters() == null) {
                    cc.setParameters(new HashMap<>());
                }
                if (registryConfig.getParameters() != null) {
                    cc.getParameters().putAll(registryConfig.getParameters());
                }
                cc.getParameters().put(CLIENT_KEY, registryConfig.getClient());
                cc.setProtocol(registryConfig.getProtocol());
                cc.setPort(registryConfig.getPort());
                cc.setAddress(getRegistryCompatibleAddress(registryConfig.getAddress()));
                cc.setNamespace(registryConfig.getGroup());
                cc.setUsername(registryConfig.getUsername());
                cc.setPassword(registryConfig.getPassword());
                if (registryConfig.getTimeout() != null) {
                    cc.setTimeout(registryConfig.getTimeout().longValue());
                }
                cc.setHighestPriority(false);
                configManager.addConfigCenter(cc);
            });
    startConfigCenter();
}

这个方法就是检查是否已经存在配置中心的信息了, 如果没有就会使用注册中心作为配置中心, 会将注册中心的很多配置信息放入ConfigCenterConfig中。并在次初始化配置中心, 这个时候至少已经有一个配置中心。

loadRemoteConfigs

从方法名称理解是加载远程的配置, 这个demo中没有找到远程的配置

private void loadRemoteConfigs() {
    // registry ids to registry configs
    //
    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;
            }));
        }
    });

    configManager.addRegistries(tmpRegistries);

    // protocol ids to protocol configs
    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.refresh();
                return protocolConfig;
            }));
        }
    });

    configManager.addProtocols(tmpProtocols);
}
checkGlobalConfigs

校验一些全局的配置是否正确,比如一些配置长度的限制,一些必须的配置是否缺失, 一些缺失的配置使用默认的配置等。 具体代码就不粘贴了。

initMetadataService

初始化源数据信息,并加载本地元数据服务提供者类

initEventListener

初始化事件监听器, dubbo 有实现一套自己的事件监听机制, 具体的会在后面文章中说明。

总结:

到这里dubbo启动的初始化流程结束, 但是具体配置的加载细节还是不明白, 比如配置文件的信息如何加载到程序中, 三个核心类中 ConfigManager 和Environment 是如何作用的机制还是不太明白, 这些疑问会留在后面的文章中说明的。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值