亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解springboot+atomikos+druid?數(shù)據(jù)庫連接失效分析

 更新時間:2022年02月08日 09:05:05   作者:kbkb  
本文主要介紹了springboot+atomikos+druid?數(shù)據(jù)庫連接失效分析,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

一、起因

  最近查看系統(tǒng)的后臺日志,經(jīng)常發(fā)現(xiàn)這樣的報錯信息:The last package successfully received from the server was 40802382 milliseconds ago,截圖如下所示。

  由于我們的系統(tǒng)都是在白天使用,夜里基本上沒有用戶使用,再加上以上的報錯信息都是出現(xiàn)在早晨,結(jié)合錯誤日志初步分析,應(yīng)該是數(shù)據(jù)庫連接超時自動斷開了。百度一番后,得知Mysql的默認(rèn)連接時間是8小時,超過8小時沒有操作后就會自動斷開連接,但是已經(jīng)使用了druid數(shù)據(jù)庫連接池,按理說已經(jīng)對數(shù)據(jù)庫連接做了保護(hù)和檢查,不應(yīng)該出現(xiàn)這樣的問題。要想徹底弄明白這個問題,就只能去研究druid數(shù)據(jù)庫連接池框架了。

二、Druid數(shù)據(jù)庫連接池

  項目的數(shù)據(jù)庫連接池基本配置信息如下所示

  通過以上的配置分析得知,一個數(shù)據(jù)庫連接從連接池中借出后經(jīng)過21600s即6小時后會被強制回收,不會超過Mysql的默認(rèn)8小時,而且也不存在這么長時間的事務(wù),所以不太可能是因為數(shù)據(jù)庫連接借出超時導(dǎo)致上面的錯誤,那么就是從數(shù)據(jù)庫連接池中申請的連接已經(jīng)超時了?似乎也不太可能,因為有檢查機(jī)制,即每隔30s就會檢查一次連接池中的連接是否超時,并且連接池中允許存在的空閑連接最大時間為540s。這就奇怪了,到底是什么原因?qū)е律厦娴腻e誤呢?這時注意到上述錯誤堆棧中的com.atomikos.datasource.pool.ConnectionPool.findOrWaitForAnAvailableConnection。是否問題的原因在于使用了Atomikos呢,帶著這樣的疑惑去閱讀了Druid和Atomikos相關(guān)的源碼。

  由于Atomikos連接池是基于Druid連接池之上的,所以Atomikos新建和銷毀數(shù)據(jù)庫連接都是從Druid連接池中借出和歸還數(shù)據(jù)庫連接,而不是直接與數(shù)據(jù)庫交互,那么我們就來看看Druid是如何維持?jǐn)?shù)據(jù)庫連接的。

public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
     //初始化檢查配置和后臺線程
        init();

        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }

從Druid連接池中獲取數(shù)據(jù)庫連接,先調(diào)用init()方法進(jìn)行初始化工作,然后調(diào)用getConnectionDirect()獲取連接。

decrementPoolingCount();
DruidConnectionHolder last = connections[poolingCount];
connections[poolingCount] = null;
DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);

public DruidPooledConnection(DruidConnectionHolder holder){
        super(holder.getConnection());

        this.conn = holder.getConnection();
        this.holder = holder;
        this.lock = holder.lock;
        dupCloseLogEnable = holder.getDataSource().isDupCloseLogEnable();
        ownerThread = Thread.currentThread();
        connectedTimeMillis = System.currentTimeMillis();
}

上述是獲取連接池中連接的關(guān)鍵代碼,即獲取connections數(shù)組中的最后一個元素,獲取到Holder后還需要將其封裝為DruidPooledConnection,這時該連接的connectedTimeMillis會被賦值為當(dāng)前時間,這個時間在后續(xù)的分析中會非常重要。

  因為配置了testWhileIdle為true,所以需要進(jìn)行下面的有效性檢查,獲取該連接的上次活躍時間,得到空閑時間,如果超過30s則做有效性檢查。

long idleMillis  = currentTimeMillis - lastActiveTimeMillis;

long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;

if (timeBetweenEvictionRunsMillis <= 0) {
      timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}

if (idleMillis >= timeBetweenEvictionRunsMillis
        || idleMillis < 0 // unexcepted branch
         ) {
     boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
     if (!validate) {
        if (LOG.isDebugEnabled()) {
             LOG.debug("skip not validate connection.");
        }

        discardConnection(poolableConnection.holder);
        continue;
        }
}
long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);

if (timeMillis >= removeAbandonedTimeoutMillis) {
    iter.remove();
    pooledConnection.setTraceEnable(false);
    abandonedList.add(pooledConnection);
}

同時,由于配置了removeAbandoned為true,所以需要檢查活躍連接是否超時,如果超時就斷開物理連接。下面看一下連接池的回收方法recycle的關(guān)鍵代碼

if (phyTimeoutMillis > 0) {
    long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
    if (phyConnectTimeMillis > phyTimeoutMillis) {
          discardConnection(holder);
          return;
    }
}
lock.lock();
try {
    if (holder.active) {
        activeCount--;
        holder.active = false;
    }
    closeCount++;

    result = putLast(holder, currentTimeMillis);
    recycleCount++;
} finally {
    lock.unlock();
}

在對數(shù)據(jù)庫連接進(jìn)行回收時,如果連接時間超過了數(shù)據(jù)庫的物理連接時間(默認(rèn)8小時)則需要斷開物理連接,否則就調(diào)用putLast方法將該連接回收到連接池。

boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
        if (poolingCount >= maxActive || e.discard) {
            return false;
        }

        e.lastActiveTimeMillis = lastActiveTimeMillis;
        connections[poolingCount] = e;
        incrementPoolingCount();

        if (poolingCount > poolingPeak) {
            poolingPeak = poolingCount;
            poolingPeakTime = lastActiveTimeMillis;
        }

        notEmpty.signal();
        notEmptySignalCount++;

        return true;
}

注意上述標(biāo)紅的地方,回收的這個連接的lastActiveTimeMillis被刷新為當(dāng)前時間,這個時間也是非常重要的,在后續(xù)分析中會用到。

三、Atomikos框架

  項目關(guān)于Atomikos的配置信息,如下所示

從上面的配置可以看出,atomikos連接池的最大連接數(shù)是25個,最小連接數(shù)是10個,連接最大的存活時間是500s,下面來看一下atomikos的源碼。

private void init() throws ConnectionPoolException
{    
     if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( this + ": initializing..." );   //如果連接池最小連接數(shù)沒有達(dá)到就新增數(shù)據(jù)庫連接
     addConnectionsIfMinPoolSizeNotReached();    //開啟維持連接池平衡的線程
     launchMaintenanceTimer();
}

以上是Atomikos初始化的部分,先補充數(shù)據(jù)庫連接池達(dá)到最小連接數(shù),然后開啟后臺線程維持連接池的平衡。

private void launchMaintenanceTimer() {
        int maintenanceInterval = properties.getMaintenanceInterval();
        if ( maintenanceInterval <= 0 ) {
            if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( this + ": using default maintenance interval..." );
            maintenanceInterval = DEFAULT_MAINTENANCE_INTERVAL;
        }
        maintenanceTimer = new PooledAlarmTimer ( maintenanceInterval * 1000 );
        maintenanceTimer.addAlarmTimerListener(new AlarmTimerListener() {
            public void alarm(AlarmTimer timer) {
                reapPool();          //如果達(dá)到了最大的存活時間就移除該連接
                removeConnectionsThatExceededMaxLifetime();          //如果沒有滿足最小連接數(shù)就新增連接
                addConnectionsIfMinPoolSizeNotReached();           //移除超過最小連接數(shù)以外的連接
                removeIdleConnectionsIfMinPoolSizeExceeded();
            }
        });
        TaskManager.SINGLETON.executeTask ( maintenanceTimer );
    }

在配置中,maintenanceInterval的值為30,即每個30秒執(zhí)行一次上述的四個方法,主要看一下removeConnectionsThatExceededMaxLifetime()這個方法。

private synchronized void removeConnectionsThatExceededMaxLifetime()
    {
        long maxLifetime = properties.getMaxLifetime();
        if ( connections == null || maxLifetime <= 0 ) return;

        if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( this + ": closing connections that exceeded maxLifetime" );

        Iterator<XPooledConnection> it = connections.iterator();
        while ( it.hasNext() ) {
            XPooledConnection xpc = it.next();
            long creationTime = xpc.getCreationTime();
            long now = System.currentTimeMillis();
            if ( xpc.isAvailable() &&  ( (now - creationTime) >= (maxLifetime * 1000L) ) ) {
                if ( LOGGER.isTraceEnabled() ) LOGGER.logTrace ( this + ": connection in use for more than " + maxLifetime + "s, destroying it: " + xpc );          //如果超過最大的存活時間就銷毀該連接
                destroyPooledConnection(xpc);
                it.remove();
            }
        }
        logCurrentPoolSize();
    }

上述方法遍歷數(shù)據(jù)庫連接池中的所有連接,如果存活時間超過maxLifetime即500s就銷毀該連接,這時由于連接池中的連接數(shù)就小于minPoolSize,所以會立即補充新的連接到連接池中。那么,系統(tǒng)在夜間沒有用戶使用時,Atomikos連接池的運行狀態(tài)為:維持最小的連接數(shù)10個數(shù)據(jù)庫連接,當(dāng)這10個連接超過500s時就會銷毀,再重新創(chuàng)建10個新的數(shù)據(jù)庫連接,不斷重復(fù)這樣的操作。

四、分析與總結(jié)

  下面我們開始分析產(chǎn)生錯誤日志的原因,當(dāng)沒有用戶使用系統(tǒng)時,Druid連接池應(yīng)該有10個空閑的連接,Atomikos連接池也有10個空閑的連接,這時Atomikos的10個連接達(dá)到了最大的生存時間500s,就需要銷毀這些連接,對于Druid來說就是回收連接,調(diào)用recycle方法。由于這10個連接應(yīng)該是500s之前從Druid連接池借出的,所以它們的connectTimeMillis也是500s之前的時間,即物理連接時間肯定小于8小時,可以成功回收到Druid連接池中,同時lastActiveTimeMillis也更新為當(dāng)前時間,放在connections數(shù)組的末尾。

  與此同時,Atomikos還需要重新生成10個新的連接,即從Druid連接池獲取10個連接,調(diào)用getConnection方法,這時會進(jìn)行有效性的檢查,又因為lastActiveTimeMillis基本上為當(dāng)前時間,所以idleMillis肯定比30s小,不需要進(jìn)行select 1的連接數(shù)據(jù)庫操作,這樣即使該連接已經(jīng)失效了還是會借出給Atomikos。每隔500s不斷循環(huán)上述操作,并且期間沒有用戶的操作,一旦超過8個小時的Mysql連接時間,Atomikos在使用數(shù)據(jù)庫連接時就會產(chǎn)生上述日志中的錯誤了。

  綜上所述,導(dǎo)致報錯的原因其實是使用了兩層數(shù)據(jù)庫連接池,這樣Druid連接池借出的數(shù)據(jù)庫連接并沒有被實際使用,這才導(dǎo)致這些數(shù)據(jù)庫連接成功躲避了Druid本身的檢查機(jī)制。

到此這篇關(guān)于springboot+atomikos+druid 數(shù)據(jù)庫連接失效分析的文章就介紹到這了,更多相關(guān)springboot+atomikos+druid 數(shù)據(jù)庫連接失效分析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文帶你搞懂Java中Get和Post的使用

    一文帶你搞懂Java中Get和Post的使用

    這篇文章主要為大家詳細(xì)介紹了Java中Get和Post用法的相關(guān)資料,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下
    2022-11-11
  • springboot jackson配置教程

    springboot jackson配置教程

    這篇文章主要介紹了springboot jackson配置教程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • spring-integration連接MQTT全過程

    spring-integration連接MQTT全過程

    這篇文章主要介紹了spring-integration連接MQTT全過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 關(guān)于重寫equals()方法和hashCode()方法及其簡單的應(yīng)用

    關(guān)于重寫equals()方法和hashCode()方法及其簡單的應(yīng)用

    這篇文章主要介紹了關(guān)于重寫equals()方法和hashCode()方法及其簡單的應(yīng)用,網(wǎng)上的知識有些可能是錯誤的,關(guān)于?equals()?方法的理解,大家討論不一樣,需要的朋友可以參考下
    2023-04-04
  • Java面向?qū)ο蠡A(chǔ)教學(xué)(二)

    Java面向?qū)ο蠡A(chǔ)教學(xué)(二)

    這篇文章主要介紹了Java的面相對象編程思想,包括類對象方法和封裝繼承多態(tài)等各個方面的OOP基本要素,非常推薦,需要的朋友可以參考下,希望可以對你有所幫助
    2021-07-07
  • java泛型基本知識和通用方法

    java泛型基本知識和通用方法

    這篇文章主要介紹了java泛型基礎(chǔ)知識及通用方法,從以下幾個方面介紹一下java的泛型: 基礎(chǔ), 泛型關(guān)鍵字, 泛型方法, 泛型類和接口,感興趣的可以了解一下
    2021-06-06
  • Spring Boot實現(xiàn)簡單的定時任務(wù)

    Spring Boot實現(xiàn)簡單的定時任務(wù)

    這篇文章主要給大家介紹了關(guān)于利用Spring Boot實現(xiàn)簡單的定時任務(wù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java Swing GridBagLayout網(wǎng)格袋布局的實現(xiàn)

    Java Swing GridBagLayout網(wǎng)格袋布局的實現(xiàn)

    這篇文章主要介紹了Java Swing GridBagLayout網(wǎng)格袋布局的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • spring自定義注解實現(xiàn)攔截器的實現(xiàn)方法

    spring自定義注解實現(xiàn)攔截器的實現(xiàn)方法

    本篇文章主要介紹了spring自定義注解實現(xiàn)攔截器的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Java 給PPT添加動畫效果的示例

    Java 給PPT添加動畫效果的示例

    這篇文章主要介紹了Java 給PPT添加動畫效果的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-04-04

最新評論