Druid連接池源碼解析(5)HighAvailableDataSource

1 HighAvailableDataSource

HighAvailableDataSource 是通過(guò)對(duì)多個(gè)DataSource的管理,來(lái)實(shí)現(xiàn)高可用的一個(gè)數(shù)據(jù)源


類圖.png

內(nèi)部用ConcurrentHashMap來(lái)存儲(chǔ)DataSource,由于只有一個(gè)無(wú)參構(gòu)造函數(shù),只能自己管理ConcurrentHashMap然后set進(jìn)去

2初始化

初始化同樣是init(),簡(jiǎn)短了很多,在DataSource上封裝了HA必要的邏輯

public void init() {
        if (inited) {
            return;
        }
        synchronized (this) {
            if (inited) {
                return;
            }
            if (dataSourceMap == null || dataSourceMap.isEmpty()) {
                poolUpdater.setIntervalSeconds(poolPurgeIntervalSeconds);
                poolUpdater.setAllowEmptyPool(allowEmptyPoolWhenUpdate);
                poolUpdater.init();
                createNodeMap();
            }
            if (selector == null) {
                setSelector(DataSourceSelectorEnum.RANDOM.getName());
            }
            if (dataSourceMap == null || dataSourceMap.isEmpty()) {
                LOG.warn("There is NO DataSource available!!! Please check your configuration.");
            }
            inited = true;
        }
    }

看到用雙檢鎖保證執(zhí)行一次,假如 dataSourceMap 為空,啟動(dòng) Updater ,動(dòng)態(tài)更新 dataSourceMap
然后主要是定義了selector,控制datasource的路由,默認(rèn)是RANDOM,此處對(duì)標(biāo)負(fù)載均衡。
從工廠類中根據(jù)name生成Selector,然后初始化

public void setSelector(String name) {
        DataSourceSelector selector = DataSourceSelectorFactory.getSelector(name, this);
        if (selector != null) {
            selector.init();
            setDataSourceSelector(selector);
        }
    }

2 DataSourceSelector

DataSourceSelector有三個(gè)實(shí)現(xiàn),一個(gè)是RandomDataSourceSelector,NamedDataSourceSelector和StickyRandomDataSourceSelector,默認(rèn)是隨機(jī)的RandomDataSourceSelector,StickyRandomDataSourceSelector就是增加了線程綁定。

  • init()中主要調(diào)用了 loadProperties()和 initThreads()倆方法。
    loadProperties 該方法會(huì)從傳入的 HighAvailableDataSource 中查找相關(guān)的配置信息,包括checkingIntervalSeconds 檢查間隔時(shí)間,recoveryIntervalSeconds 蘇醒間隔時(shí)間,validationSleepSeconds 驗(yàn)證睡眠時(shí)間,blacklistThreshold 黑名單閾值。

    • initThreads 根據(jù)配置初始化線程,這里主要涉及到兩條線程 validateThread & recoverThread 。
    • validateThread 的實(shí)現(xiàn)類是RandomDataSourceValidateThread,就是一個(gè)繼承Runnable的線程,主要是檢查 dataSourceMap 中的 datasource 是否可用
 public void run() {
        while (true) {
            if (selector != null) {
                checkAllDataSources();
                maintainBlacklist();
                cleanup();
            } else {
                break;
            }
            sleepForNextValidation();
        }
    }

checkAllDataSources 檢查 datasource 是否正常,這里會(huì)根據(jù)檢查時(shí)間看是否需要跳過(guò),主要是根據(jù) checkingIntervalSeconds 來(lái)判斷,接著到了真正檢查的邏輯,他會(huì)先從 datasource 中獲取鏈接的信息,并新建一條鏈接,而非重 datasource 中獲取,然后使用這個(gè)鏈接執(zhí)行一條指令,在 MySQL 中是執(zhí)行 pingInternal 方法。
檢查上面的執(zhí)行檢查結(jié)果是否成功,假如成功,就從 blacklist 中移除,并重置 errorcounter ,假如不成功且失敗次數(shù)大于 blacklistThreshold 黑名單閾值,將其放入 blacklist 中,等待后續(xù)操作。
cleanup 根據(jù) successTimes , errorCounts 和 lastCheckTimes 來(lái)清理 datasource,將其移入 blacklist 中

  • recoverThread同樣是實(shí)現(xiàn)了Runnable的RandomDataSourceRecoverThread,主要主要是 tryOneDataSource 嘗試將 datasource 將其移出 blacklist 。檢查的邏輯和 validateThread 的 checkAllDataSources 基本一致,即嘗試啟動(dòng)一個(gè)新的鏈接檢查該數(shù)據(jù)源是否正常。

  • get()方法是調(diào)用方獲取連接時(shí),真正獲取到目標(biāo)Datasource的方法,主要先將黑名單的和 busy 的DataSource 移除,這里校驗(yàn)是否 busy ,只檢查改 DataSource 的 poolcount 是否 <= 0; 假如是就代表空閑連接為 0 。最后在 getRandomDataSource() 通過(guò) radom 函數(shù)求余獲取到真正的 DataSource

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容