使用Apollo动态修改线上数据源

前言

  最近需要实现一个功能,动态刷新线上数据源环境,下面来使用Apollo配置中心和Spring提供的AbstractRoutingDataSource来实现。

具体实现

  Apollo是携程开源的统一配置中心,和springboot无缝衔接并且不需要安装其他软件就可以直接使用,可以实时推送最新的配置文件。Spring提供的AbstractRoutingDataSource用于动态管理数据源,可以动态更新数据源,一般数据库的读写分离也是用这个抽象类实现的。

  对Apollo不熟悉的可以先了解一下,GitHub:https://github.com/ctripcorp/apollo

  关于AbstractRoutingDataSource,介绍一下我们用到的方法

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
    
    //传入的数据源
    private Map<Object, Object> targetDataSources;

     //拿着子类实现的determineCurrentLookupKey()方法的返回值当做key在这个Map中寻找数据源
    private Map<Object, DataSource> resolvedDataSources;

    //放入多个数据源
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        this.targetDataSources = targetDataSources;
    }

 
    //属性设置完成后执行
    public void afterPropertiesSet() {
        if (this.targetDataSources == null) {
            throw new IllegalArgumentException("Property 'targetDataSources' is required");
        } else {
            this.resolvedDataSources = new HashMap(this.targetDataSources.size());
            Iterator var1 = this.targetDataSources.entrySet().iterator();

            while(var1.hasNext()) {
                Entry<Object, Object> entry = (Entry)var1.next();
                Object lookupKey = this.resolveSpecifiedLookupKey(entry.getKey());
                DataSource dataSource = this.resolveSpecifiedDataSource(entry.getValue());
                this.resolvedDataSources.put(lookupKey, dataSource);
            }

            if (this.defaultTargetDataSource != null) {
                this.resolvedDefaultDataSource = this.resolveSpecifiedDataSource(this.defaultTargetDataSource);
            }

        }
    }

    protected Object resolveSpecifiedLookupKey(Object lookupKey) {
        return lookupKey;
    }


    //子类要实现的抽象方法,数据源的获取策略
    protected abstract Object determineCurrentLookupKey();
}

  下面来实现通过Apollo动态修改数据源:

@Configuration
public class DataSourceConfiguration {


    private final static String DATASOURCE_TAG = "db";

    @Autowired
    ApplicationContext context;

    @ApolloConfig
    Config config;

    @Bean("dataSource")
    public DynamicDataSource dynamicDataSource() {
     //使用springboot默认的连接池 DynamicDataSource source = new DynamicDataSource(); //只有一个数据源,传入的Map的key为db,value为使用的数据源 source.setTargetDataSources(Collections.singletonMap(DATASOURCE_TAG, dataSource())); return source; } //Apollo监听配置是否修改 @ApolloConfigChangeListener public void onChange(ConfigChangeEvent changeEvent) { SetchangedKeys = changeEvent.changedKeys(); if (changedKeys.contains("spring.datasource.url")) { DynamicDataSource source = context.getBean(DynamicDataSource.class); //当检测到数据库地址改变时,重新设置数据源 source.setTargetDataSources(Collections.singletonMap(DATASOURCE_TAG, dataSource())); //调用该方法刷新resolvedDataSources,下次获取数据源时将获取到新设置的数据源 source.afterPropertiesSet(); } } public DataSource dataSource() { HikariDataSource dataSource = new HikariDataSource(); dataSource.setJdbcUrl(config.getProperty("spring.datasource.url", "")); dataSource.setUsername(config.getProperty("spring.datasource.username", "")); dataSource.setPassword(config.getProperty("spring.datasource.password", "")); return dataSource; } //简单实现AbstractRoutingDataSource,因为只是有一个数据源,所以任何时候选择的数据源都一样 class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DATASOURCE_TAG; } } }

  

 

determineCurrentLookupKey
posted @ 2019-11-26 23:20  卫旗  阅读(4065)  评论(1编辑  收藏  举报