seata-config模块解析

简介

    分布式事务解决方案,找个成熟的方案看看,得谈谈seata。看看其源码,主要模块有core、server、tm、rm、tcc、config、discovery。。。,还挺多的,没办法要搞懂这个技术就得一个个看看,其实是看server的时候对config有了疑问,因此才转而想把config优先弄懂。

浅析

    之前说到看server的时候有了疑问,那就是为什么分布式配置时nacos要用那个脚本(nacos-config.sh)把单值一个个配置项加载进nacos,而不是直接在nacos搞一个诸如seata.yml这样的文件配置呢?带着这个疑问开启seata-config探索之路,看看它的pom文件,不难看出seata-config-core就是其核心模块,然而知道核心模块好像也没啥太大作用啊,一看一堆接口和类,看一个都明白,看一堆就玩不转了。那这个简单了,看看server模块里边怎么用的,下边是server启动类的一部分代码。

public static void main(String[] args) throws IOException {
        ParameterParser parameterParser = new ParameterParser(args);
        NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(WORKING_THREADS);
        //server port
        nettyRemotingServer.setListenPort(parameterParser.getPort());
        //log store mode : file, db, redis
        SessionHolder.init(parameterParser.getStoreMode());
        DefaultCoordinator coordinator = new DefaultCoordinator(nettyRemotingServer);
        coordinator.init();
        nettyRemotingServer.setHandler(coordinator);
    }

貌似没有config啥玩意啊,继续看看ParameterParser、NettyBaseConfig。

//ParameterParser
storeMode = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE,
                    SERVER_DEFAULT_STORE_MODE);

//NettyBaseConfig
protected static final Configuration CONFIG = ConfigurationFactory.getInstance();
RANSPORT_SERVER_TYPE = TransportServerType.getType(CONFIG.getConfig(ConfigurationKeys.TRANSPORT_SERVER, TransportServerType.NIO.name()));

可以看到就是通过配置工厂去获取配置,然后通过配置去获取配置项。貌似config模块提供了工厂方法对外暴露Configuration来提供配置获取服务,那么好了正式进入正题,从ConfigurationFactory来研究config。

static {
        load();
    }

    private static void load() {
        String seataConfigName = System.getProperty(SYSTEM_PROPERTY_SEATA_CONFIG_NAME);
        if (seataConfigName == null) {
            //registry
            seataConfigName = REGISTRY_CONF_DEFAULT;
        }
        String envValue = System.getProperty(ENV_PROPERTY_KEY);
        //1
        Configuration configuration = (envValue == null) ? new     FileConfiguration(seataConfigName,
                false) : new FileConfiguration(seataConfigName + "-" + envValue, false);
        Configuration extConfiguration = null;
        try {
            //2
            extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
        } catch (EnhancedServiceNotFoundException ignore) {

        } catch (Exception e) {
            LOGGER.error("failed to load extConfiguration:{}", e.getMessage(), e);
        }
        //3
        CURRENT_FILE_INSTANCE = extConfiguration == null ? configuration : extConfiguration;
    }

这段静态初始化代码就是搞一个CURRENT_FILE_INSTANCE对象,其中1是加载config.conf或其他通过环境指定的获取Configuration,2是通过spi机制增强Configuration,如SpringBootConfigurationProvider。继续看看如何获取单例的。

private static Configuration buildConfiguration() {
        ConfigType configType;
        String configTypeName;
        try {
            //1
            configTypeName = CURRENT_FILE_INSTANCE.getConfig(
                    ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR
                            + ConfigurationKeys.FILE_ROOT_TYPE);
            configType = ConfigType.getType(configTypeName);
        } catch (Exception e) {
            throw e;
        }
        Configuration extConfiguration = null;
        Configuration configuration;
        if (ConfigType.File == configType) {
            String pathDataId = String.join(ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR,
                    ConfigurationKeys.FILE_ROOT_CONFIG, FILE_TYPE, NAME_KEY);
            String name = CURRENT_FILE_INSTANCE.getConfig(pathDataId);
            //2
            configuration = new FileConfiguration(name);
            try {
                extConfiguration = EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);
            } catch (EnhancedServiceNotFoundException ignore) {

            } catch (Exception e) {
                LOGGER.error("failed to load extConfiguration:{}", e.getMessage(), e);
            }
        } else {
            //2
            configuration = EnhancedServiceLoader
                    .load(ConfigurationProvider.class, Objects.requireNonNull(configType).name()).provide();
        }
        try {
            Configuration configurationCache;
            if (null != extConfiguration) {
                configurationCache = ConfigurationCache.getInstance().proxy(extConfiguration);
            } else {
                //3
                configurationCache = ConfigurationCache.getInstance().proxy(configuration);
            }
            if (null != configurationCache) {
                extConfiguration = configurationCache;
            }
        } catch (EnhancedServiceNotFoundException ignore) {

        } catch (Exception e) {
            LOGGER.error("failed to load configurationCacheProvider:{}", e.getMessage(), e);
        }
        //4
        return null == extConfiguration ? configuration : extConfiguration;
    }

     可以看到通过CURRENT_FILE_INSTANCE来获取configType,进而根据不同的configType来获取Configuration,2通过spi机制获取或增强Configuration,3来获取Configuration的代理对象,这个ConfigurationCache很简单就是提供一个本地缓存支持。

    到这里ConfigurationFactory就差不多分析完了,简单总结下这个对象:1、静态初始化来加载配置文件(如registry.conf)来获取CURRENT_FILE_INSTANCE,并且通过spi提供扩展。2、由CURRENT_FILE_INSTANCE提供的configType来获取Configuration,并且做了代理处理,来提供缓存支持。

    接下来就看看Configuration吧,这个接口很简单,就是对配置项的管理对象。看nacos的具体实现。

private static volatile ConfigService configService;
public String getLatestConfig(String dataId, String defaultValue, long timeoutMills) {
        String value = getConfigFromSysPro(dataId);
        if (value != null) {
            return value;
        }
        try {
            value = configService.getConfig(dataId, getNacosGroup(), timeoutMills);
        } catch (NacosException exx) {
            LOGGER.error(exx.getErrMsg());
        }
        return value == null ? defaultValue : value;
    }

这个就是委托给ConfigService,再看看ConfigService。

private final ClientWorker worker;
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
        ConfigResponse cr = new ConfigResponse();
        
        // 优先使用本地配置
        String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
        if (content != null) {
            cr.setContent(content);
            configFilterChainManager.doFilter(null, cr);
            content = cr.getContent();
            return content;
        }
        
        try {
            String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
            cr.setContent(ct[0]);
            
            configFilterChainManager.doFilter(null, cr);
            content = cr.getContent();
            
            return content;
        } catch (NacosException ioe) {
            if (NacosException.NO_RIGHT == ioe.getErrCode()) {
                throw ioe;
            }
            
        }
        
        content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
        cr.setContent(content);
        configFilterChainManager.doFilter(null, cr);
        content = cr.getContent();
        return content;
    }

这个就是委托ClientWorker去拿配置,那继续看看ClientWorker吧。

public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
            throws NacosException {
        String[] ct = new String[2];
        if (StringUtils.isBlank(group)) {
            group = Constants.DEFAULT_GROUP;
        }
        
        HttpRestResult<String> result = null;
        try {
            Map<String, String> params = new HashMap<String, String>(3);
            if (StringUtils.isBlank(tenant)) {
                params.put("dataId", dataId);
                params.put("group", group);
            } else {
                params.put("dataId", dataId);
                params.put("group", group);
                params.put("tenant", tenant);
            }
            result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
        } catch (Exception ex) {
            String message = String
                    .format("[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s",
                            agent.getName(), dataId, group, tenant);
        }
        
        switch (result.getCode()) {
            case HttpURLConnection.HTTP_OK:
                LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.getData());
                ct[0] = result.getData();
                if (result.getHeader().getValue(CONFIG_TYPE) != null) {
                    ct[1] = result.getHeader().getValue(CONFIG_TYPE);
                } else {
                    ct[1] = ConfigType.TEXT.getType();
                }
                return ct;
            
        }
    }

这里就比较清楚了,就是到nacos去拿配置。但这个组件还有点内容,还要做个长轮询拉取。大致看看这个组件。

这个简单说吧,就是定时的拉取nacos服务端数据缓存到本地。

这里简单对nacos-config做下小结:NacosConfiguration委托ConfigService去获取配置,NacosConfigService委托ClientWorker去获取配置,ClientWorker结合nacos的api去服务端拉取配置。

总结

    到了这里大致就分析完seata-config模块了,ConfigurationFactory加载配置文件+spi规定配置的获取,Configuration规定配置项的获取。不同的配置提供者通过对ConfigurationProvider和Configuration的实现来实现配置提供服务,那进一步提一下,如果要nacos支持如:seata.yml这样的从配置中心获取的文件,再从文件中取得配置应该怎么做呢?(说明:实在觉得一项一项的在配置中心的配置太烦琐了),当然其实seata-config-spring-cloud已经支持了在environment中获取配置了,也就是说结合springcloud已经可以在配置中心拿配置文件来获取seata配置了,当然只限于CURRENT_FILE_INSTANCE(严格来说是SpringBootConfigurationProvider提供的支持)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&一步

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值