第三章 shardingsphere源码-项目启动前到底干了些什么?


一、项目启动核心源码

上一篇文章说了项目是如何启动的,仍然与springboot一样,找到启动类Bootstarp.java,本篇讲讲启动之前到底干了些什么,还是使用的web服务器吗如tomcat等?

1.ProxyConfigurationLoader.load 就是将我们填写的配置文件server.yaml 或者 config-encrypt.yaml 来解析出来封装为YamlProxyConfiguration 对象。
2.下面的port 、addresses 都是配置文件中定义的,项目的端口是什么
3.核心方法 new BootstrapInitializer().init 初始化了些什么,具体参考下面
4.new ShardingSphereProxy().start(port, addresses); 使用了netty作为服务器

    public static void main(final String[] args) throws IOException, SQLException {
        BootstrapArguments bootstrapArgs = new BootstrapArguments(args);
        // 读取配置文件,创建Yaml对象
        YamlProxyConfiguration yamlConfig = ProxyConfigurationLoader.load(bootstrapArgs.getConfigurationPath());
        int port = bootstrapArgs.getPort().orElseGet(() -> new ConfigurationProperties(yamlConfig.getServerConfiguration().getProps()).getValue(ConfigurationPropertyKey.PROXY_DEFAULT_PORT));
        List<String> addresses = bootstrapArgs.getAddresses();
        // 初始化 -如:将配置持久化到存储设备等等
        new BootstrapInitializer().init(yamlConfig, port, bootstrapArgs.getForce());
        boolean cdcEnabled = null != yamlConfig.getServerConfiguration().getCdc() && yamlConfig.getServerConfiguration().getCdc().isEnabled();
        if (cdcEnabled) {
            new CDCServer(addresses, yamlConfig.getServerConfiguration().getCdc().getPort()).start();
        }
        // 类似netty启动,里面注册了很多消息处理器,规定了客户端发送消息服务端到底如何接收
        new ShardingSphereProxy().start(port, addresses);
    }

1.核心方法 new BootstrapInitializer().init

其中唯一的核心方法就是createContextManager(proxyConfig, modeConfig, port, force);创建上下文的管理器,剩下的几乎都是将配置信息封装为元数据对象。

    public void init(final YamlProxyConfiguration yamlConfig, final int port, final boolean force) throws SQLException {
        ModeConfiguration modeConfig = null == yamlConfig.getServerConfiguration().getMode() ? null : new YamlModeConfigurationSwapper().swapToObject(yamlConfig.getServerConfiguration().getMode());
        ProxyConfiguration proxyConfig = new YamlProxyConfigurationSwapper().swap(yamlConfig);
        ContextManager contextManager = createContextManager(proxyConfig, modeConfig, port, force);
        ProxyContext.init(contextManager);
        contextManagerInitializedCallback(modeConfig, contextManager);
        ShardingSphereProxyVersion.setVersion(contextManager);
    }

=》createContextManager()
首先将所有配置信息-比如数据源信息、加密信息、整个服务的全局配置信息全部封装到ContextManagerBuilderParameter 这个总配置类中,接下来使用了Java的SPI机制来获取 到底是否使用配置中心来将配置进行持久化,根据在Server.yaml中的
mode:
type: Cluster
来进行区分,使用配置中心就走ClusterContextManagerBuilder,

    private ContextManager createContextManager(final ProxyConfiguration proxyConfig, final ModeConfiguration modeConfig, final int port, final boolean force) throws SQLException {
        // 将配置文件中的 各个参数封装为对象,比如 加密信息、server.yaml中各个配置信息等 封装到 ContextManagerBuilderParameter ,总配置类
        ContextManagerBuilderParameter param = new ContextManagerBuilderParameter(modeConfig, proxyConfig.getDatabaseConfigurations(),
                proxyConfig.getGlobalConfiguration().getRules(), proxyConfig.getGlobalConfiguration().getProperties(), proxyConfig.getGlobalConfiguration().getLabels(),
                createInstanceMetaData(proxyConfig, port), force);
        ContextManagerBuilder contextManagerBuilder = null == modeConfig
                ? RequiredSPIRegistry.getRegisteredService(ContextManagerBuilder.class)
                : TypedSPIRegistry.getRegisteredService(ContextManagerBuilder.class, modeConfig.getType());
        return contextManagerBuilder.build(param);
    }

第一个使用SPI的地方,用来区分
=》ClusterContextManagerBuilder.build()
1.getClusterPersistRepository() ,判断是哪一种配置中心,并执行init初始化,建立连接继续使用第二个SPI机制,来获取某个配置中心,以ZK为例,使用的是 CuratorFramework 来操作ZK,使用CuratorFrameworkFactory.builder()来设置zk的连接信息、超时时间、重试次数,以及新增、修改、删除等操作封装为 ZookeeperRepository。
目前支持的种类
在这里插入图片描述
2.persistConfigurations(),将之前配置信息封装的ContextManagerBuilderParameter 对象进行持久化操作,根路径为proxy_1,然后比如全局的规则信息 /proxy_1/rules ,全局的参数信息 /proxy_1/rules
3.继续下面就是RegistryCenter 注册中心的创建,里面定义了 使用的配置中心信息、以及调用zk在线注册方法、zk中节点发生变化watch机制、根据某个key从zk中获取信息。
在这里插入图片描述
4. persistMetaData()
向ZK中持久化元数据信息,这部分信息包括代理数据库,这个库下面的所有表以及字段信息、索引信息
这两部分代码一个是对表持久化、一个是对表下面的字段进行持久化

    public void persist(final String databaseName, final String schemaName, final ShardingSphereSchema schema) {
        if (schema.getTables().isEmpty() && schema.getViews().isEmpty()) {
            addSchema(databaseName, schemaName);
        }
        tableMetaDataPersistService.persist(databaseName, schemaName, schema.getTables());
    }
        private void persistTableData(final String databaseName, final String schemaName, final String tableName, final Collection<YamlShardingSphereRowData> rows) {
        tableRowDataPersistService.persist(databaseName, schemaName, tableName, rows);
    }

5.setContextManagerAware() 继续向下又创建一个上下文管理器,将元数据信息和实例信息封装,然后设置到 ClusterModeContextManager ,整个集群的上下文管理器

    @Override
    public ContextManager build(final ContextManagerBuilderParameter param) throws SQLException {
        // 判断是哪一种配置中心,并执行init初始化,建立连接
        ClusterPersistRepository repository = getClusterPersistRepository((ClusterPersistRepositoryConfiguration) param.getModeConfiguration().getRepository());
        MetaDataPersistService persistService = new MetaDataPersistService(repository);
        // 将配置信息进行持久化,也就是conf下的配置文件,实际上都是server.yaml 以及 config-encry.yaml的配置信息
        persistConfigurations(persistService, param);
        // 定义了注册中心对象,里面大体都是操作zk的
        RegistryCenter registryCenter = new RegistryCenter(repository, new EventBusContext(), param.getInstanceMetaData(), param.getDatabaseConfigs());
        InstanceContext instanceContext = buildInstanceContext(registryCenter, param);
        if (registryCenter.getRepository() instanceof InstanceContextAware) {
            ((InstanceContextAware) registryCenter.getRepository()).setInstanceContext(instanceContext);
        }
        MetaDataContexts metaDataContexts = MetaDataContextsFactory.create(persistService, param, instanceContext, registryCenter.getStorageNodeStatusService().loadStorageNodes());
        persistMetaData(metaDataContexts); // 持久化:这个库下面的所有表信息、字段信息、主键等信息
        ContextManager result = new ContextManager(metaDataContexts, instanceContext);
        setContextManagerAware(result);
        registerOnline(persistService, registryCenter, param, result);
        return result;
    }

=>registerOnline()
其中前面的几个方法都是向zk中注册信息,比如应用启动的时候的版本、它的一个唯一id信息,目前来说没什么大用.但是其中最后两个方法很重要。
6.new ContextManagerSubscriberFacade(persistService, registryCenter, contextManager);
在这里面创建了一系列的订阅者,来订阅当zk中的配置得到修改后就会发生变化,比如 zk中的/proxy_1/metadata/postgres/versions/0/data_sources 中的配置内容发送变化时,首先基于zk的watch机制监听,然后由谷歌的eventbus的post进行通知。

    public ContextManagerSubscriberFacade(final MetaDataPersistService persistService, final RegistryCenter registryCenter, final ContextManager contextManager) {
        new ConfigurationChangedSubscriber(persistService, registryCenter, contextManager);
        new ResourceMetaDataChangedSubscriber(contextManager);
        new DatabaseChangedSubscriber(contextManager);
        new StateChangedSubscriber(registryCenter, contextManager);
        new ProcessListChangedSubscriber(registryCenter, contextManager);
        new CacheEvictedSubscriber(contextManager.getInstanceContext().getEventBusContext());
    }

7.registryCenter.onlineInstance()也是注册信息,只不过注册的是自己的节点信息,CuratorCache
主要用于缓存ZooKeeper中的节点信息。可以对单个节点信息进行缓存,也可以对子树(包括该节点及其所有子节点)的信息进行缓存。CuratorCache还支持注册监听器,这样在特定事件发生时,如节点信息发生变化等,就可以实时收到通知。

    public void watch(final String key, final DataChangedEventListener listener) {
        CuratorCache cache = caches.get(key);
        if (null == cache) {
            cache = CuratorCache.build(client, key);
            caches.put(key, cache);
        }
		// client代表了与ZooKeeper服务器的连接,key则指定了需要缓存的节点路径,而listener则用于监听节点的变化事件。
        CuratorCacheListener curatorCacheListener = CuratorCacheListener.builder()
                .forTreeCache(client, (framework, treeCacheListener) -> { // 对treeCache进行监听
                    Type changedType = getChangedType(treeCacheListener.getType()); // 获取变更类型
                    if (Type.IGNORED != changedType) { //  如果不是忽略类型
                        listener.onChange(new DataChangedEvent(treeCacheListener.getData().getPath(),  // 发出变更事件
                                new String(treeCacheListener.getData().getData(), StandardCharsets.UTF_8), changedType));
                    }
                }).build();
        cache.listenable().addListener(curatorCacheListener);
        start(cache);
    }
    private void registerOnline(final MetaDataPersistService persistService, final RegistryCenter registryCenter, final ContextManagerBuilderParameter param, final ContextManager contextManager) {
        contextManager.getInstanceContext().getInstance().setLabels(param.getLabels());
        contextManager.getInstanceContext().getAllClusterInstances().addAll(registryCenter.getComputeNodeStatusService().loadAllComputeNodeInstances());
        new ContextManagerSubscriberFacade(persistService, registryCenter, contextManager); // 创建上下文管理 ,创建一系列对象订阅 如数据源修改等
        registryCenter.onlineInstance(contextManager.getInstanceContext().getInstance()); // 注册一下当前节点的信息,持久化到 zk,也就是zk配置中 nodes 节点 ,并且使用zk的watch机制
    }

2.总结

本篇主要介绍了,再真正的启动netty时之前所做的准备工作,1.将配置信息解析成对象 2.将配置信息进行持久化
里面使用了很多层对象来进行封装,比较复杂,并且使用了Java的spi机制来降低系统的耦合,不想在硬编码里面写死具体的实现,而是希望由服务提供者来选择使用那种实现方式。
所以阅读这部分源码学会了,如果当有很多个实例类时,可以使用Java spi机制,来降低系统的耦合。

后续如何启动netty敬请下回分解:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值