A01-多數據源庫的簡單分析-dynamic-datasource-spring-boot-starter庫的研究


最近在查看動態多數據源,看到了dynamic-datasource-spring-boot-starter庫,地址在:https://github.com/baomidou/dynamic-datasource-spring-boot-starter

 

這里進行簡單分析,學習其基本原理。

 

一、注解的引入

這個庫,要求通過DS注解,在自己的業務代碼的方法上,聲明使用哪種數據源。

//如果引入了jar包,通過 spring的META-INF/spring.factories自動引入配置 DynamicDataSourceAutoConfiguration

//DynamicDataSourceAutoConfiguration 類,注冊了Bean DynamicDataSourceAnnotationAdvisor,這里實現了對DS注解的攔截器 DynamicDataSourceAnnotationInterceptor

 

二、注解引起的攔截器

/**
* 這里通過一個線程上下文變量實現了 數據源坐標的傳遞。
* 並且帶有推斷的功能,如果是#開頭的數據源坐標,可以在運行時,通過DsProcessor 進行推斷,推斷結果,放入dsKey中。
* 如果是普通數據源,則直接放在線程上下文里。 這里使用了Deque 來存儲,跟隨着調用線程棧的變化而變化(如果遇到了DS注解,也會入棧出棧)。
*

*/
public
class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { try { String dsKey = determineDatasourceKey(invocation); DynamicDataSourceContextHolder.push(dsKey); return invocation.proceed(); } finally { DynamicDataSourceContextHolder.poll(); } } }
/**
* 這里為止,只是在線程里,放了數據源的坐標
*/

 

三、mybatis插件的配合

/**
* 這里使用了mybatis的插件規范,增加@Intercepts注解,聲明攔截mybatis的什么類、什么方法、什么入參。
* 因為 增、刪、改 都是走的update,這里攔截了mybatis的Executor的query和update方法
*
* 這里主要處理讀寫分離之類的需求,如果當前沒有指定數據源(線程上下文沒有設置),就嘗試主備配置。
* 對於多數據源切換之類的需求,這里可以跳過,沒有實質邏輯
*/

@Intercepts({ @Signature(type
= Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) @Slf4j public class MasterSlaveAutoRoutingPlugin implements Interceptor { @Autowired private DynamicDataSourceProperties properties; @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; boolean empty = true; try { empty = StringUtils.isEmpty(DynamicDataSourceContextHolder.peek()); if (empty) { DynamicDataSourceContextHolder.push(getDataSource(ms)); } return invocation.proceed(); } finally { if (empty) { DynamicDataSourceContextHolder.clear(); } } } }

 

四、動態數據源

/**
* 這里聲明了一個動態數據源,實現了DataSource,注冊給外部使用。如果使用了mybatis的話,mybatis在獲取connection的時候,會在這里獲取connection
* 
*
*/


@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource implements InitializingBean, DisposableBean {
    //通過線程變量,配合出來,獲取最新的一次設置的數據源坐標(函數調用存在嵌套,此時數據源坐標的Deque可能入棧了好幾個坐標了)
    @Override
    public DataSource determineDataSource() {
        return getDataSource(DynamicDataSourceContextHolder.peek());
    }

    //通過DataSource獲取connection
    @Override
    public Connection getConnection() throws SQLException {
        return determineDataSource().getConnection();
    }
    //做選擇策略
    public DataSource getDataSource(String ds) {
        if (StringUtils.isEmpty(ds)) {
            return determinePrimaryDataSource();
        } else if (!groupDataSources.isEmpty() && groupDataSources.containsKey(ds)) {
            log.debug("dynamic-datasource switch to the datasource named [{}]", ds);
            return groupDataSources.get(ds).determineDataSource();
        } else if (dataSourceMap.containsKey(ds)) {
            log.debug("dynamic-datasource switch to the datasource named [{}]", ds);
            return dataSourceMap.get(ds);
        }
        if (strict) {
            throw new RuntimeException("dynamic-datasource could not find a datasource named" + ds);
        }
        return determinePrimaryDataSource();
    }

 

五、總結

支持多個數據源,支持動態添加、刪除數據源,而且有一些兜底策略;可以根據條件,用代碼動態選擇數據源,支持主備讀寫分離。 感謝原作者的貢獻和開源。

 

和我具體業務的需求的有一點不同:需要動態控制數據源的創建、檢測和刪除,暫時空閑的數據源連接池要刪除;提供一個批量任務執行框架,可以批量並發執行SQL操作,而且控制其並發度,不超過本地連接池的連接數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM