前言java
最近須要實現一個功能,動態刷新線上數據源環境,下面來使用Apollo配置中心和Spring提供的AbstractRoutingDataSource來實現。git
具體實現github
Apollo是攜程開源的統一配置中心,和springboot無縫銜接而且不須要安裝其餘軟件就能夠直接使用,能夠實時推送最新的配置文件。Spring提供的AbstractRoutingDataSource用於動態管理數據源,能夠動態更新數據源,通常數據庫的讀寫分離也是用這個抽象類實現的。spring
對Apollo不熟悉的能夠先了解一下,GitHub:https://github.com/ctripcorp/apollo數據庫
關於AbstractRoutingDataSource,介紹一下咱們用到的方法springboot
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動態修改數據源:ide
@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