關於CLOSE BY CLIENT STACK TRACE

關於CLOSE BY CLIENT STACK TRACE

 

程序正常運行,數據庫連接可以獲取,一些列操作都可以實現,可在debug信息中總會一段時間就報如下錯誤:

 

java.lang.Exception : DEBUG -- CLOSE BY CLIENT STACK TRACE

at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:566 )

    at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:234 )

    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:470 )

    at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:964 )

    at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547 )


跟蹤錯誤代碼,發現 c3p0 內部的異常輸出(紅色部分

C3p0 相關源碼

 com.mchange.v2.c3p0.impl. NewPooledConnection

public synchronized void close() throws SQLException

{ close( null ); }

 

private void close( Throwable cause ) throws SQLException

    { close( cause, false ); }

 

private void close( Throwable cause, boolean forced ) throws SQLException

    {

。。。。。。。。。。。。。。。。 

            if ( cause == null )

            {

                this .invalidatingException = NORMAL_CLOSE_PLACEHOLDER ;

 

                if ( Debug.DEBUG && logger .isLoggable( MLevel.FINEST ) )

                    logger .log( MLevel.FINEST , this + " closed by a client.", new Exception("DEBUG -- CLOSE BY CLIENT STACK TRACE") );

 

                logCloseExceptions ( null , closeExceptions );

 

                if (closeExceptions.size() > 0)

                    throw new SQLException("Some resources failed to close properly while closing " + this );

            }

            else

            {

                this .invalidatingException = cause;

                if (Debug.TRACE >= Debug.TRACE_MED )

                    logCloseExceptions ( cause, closeExceptions );

                else

                    logCloseExceptions ( cause, null );

            }

        }

    }


爲何會有這樣的錯誤,一頭霧水。


看了下配置文件,覺得可能是automaticTestTable配置引起的,將其註釋掉,再運行,發現錯誤不再出現。

看了下關於 automaticTestTable的作用:

 由於Mysql服務器默認的wait_timeout是8小時,也就是說一個connection空閒超過8個小時,
    Mysql將自動斷開該 connection。然而在C3P0 pools中的connections如果空閒超過8小時,
    Mysql將其斷開,而C3P0並不知道該connection已經失效,如果這時有 Client請求connection,
    C3P0將該失效的Connection提供給Client,將會造成異常。
    解決方法可爲:1.jdbcUrl上面加一個autoReconnect=true
     2.由於autoReconnect=true 在新的connector/J版本里面已經deprecated了,文檔裏面建議不要使用。
       所以配置參數idleConnectionTestPeriod,automaticTestTable


而配置 automaticTestTable卻 會報CLOSE BY CLIENT STACK TRACE,故選擇配置preferredTestQuery 而不是 automaticTestTable 。


以上只是在使用c3p0是發現的問題,不知道是否理解正確,不過其正真原因到目前還是弄不清楚,望解答!

 

補充:今天在http://hi.baidu.com/xhr8334/blog/item/cf15d1a6deb235fc9052ee9b.html看到一個見解:(引用主題內容)

我看到那個DEBUG,我說,是調試信息,修改一下LOG4J的等級就行了。

這個羣友很不解的問,既然成功了,幹嘛還要丟異常出來?

這裏就不得不說到兩個商業開發的原則問題了。

第一,對上家傳入數據嚴加過濾,對傳出給下家的數據仔細檢查。

第二,合理使用異常。

第一點其實很簡單的。也就是模塊化開發的一個思想問題。對自己的行爲負責。前端返回的數據究竟是什麼,需要進行校驗。不合格的剔除或者是修正。合格的處理完後,在傳出之前也要加以校驗,是否合格。

具體到這個問題裏,就是來自Spring關於數據源的那個配置文件。

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

//略去數據源相關信息的配置

</bean>

這個配置文件裏,有兩個信息是很管用的。一個是class,一個是destroy-method。簡單點說,一個是數據源的實現類,一個是析構方法。Spring在讀取這個配置文件以後,需要根據這些信息來實例化一些類,然後內部再根據中間的那些配置信息來實際構造數據源。比如username啥的。

可是來了個問題。不能保證這裏的ComboPooledDataSource數據源一定是可用的,也不能保證close方法一定能關閉連接,對吧?Spring本身不能檢查這個類是否真實有效,毫無Bug。實際上呢,也檢查不了。同樣的,close方法是否有效,也需要進行檢查。這就是我剛纔說的,對上家數據的嚴加檢查。

那好吧,怎麼檢查呢?最簡潔的方法莫過於實際構造一下,連接池,獲取數據庫連接,執行一個測試語句,然後關閉連接。如果一切都成功,那就OK。關於那個測試語句,配置過WebSphere數據源的同學們還記得不?有個SQL語句會默認的寫在數據源配置裏,是 " SELECT 1 FROM TABLE "。嗯,對的,這個就是測試語句。

這一套流程能走得通,走的順,那麼就可以在自己能力範圍內說這個數據源和連接池是能用的,對吧?

這裏補充一個知識。java.sql.Connection,這玩意不是class,是interface。

聲明是:public interface Connection extends Wrapper 。

任何一個JDBC數據庫連接的實現類都應該實現這個接口的全部方法。比如,close。API裏的描述是,立即釋放此 Connection 對象的數據庫和 JDBC 資源,而不是等待它們被自動釋放。

熟悉Java的同學們應該記得一點,在規範裏有個要求,就是接口的實現類必須實現接口的所有方法。但是呢,這句話還有個意思,那就是,你可以在實現所有方法之外,再寫幾個方法。沒人會管你。

啊哈,那就有疑問了。雖然API規定了close是關閉連接釋放資源的。但這只是你接口的一廂情願。也許人家實現廠家覺得close方法不夠帥,要改成closeConnection。那。。。Spring總不好傻傻的去死扣close方法來關閉連接吧?雖然這方法必須實現,但是可沒說一定要有內容啊。如果是空方法呢?

所以有了destroy-method這個配置項的出現。Spring說,不礙的,您老人家看哪個爽,告訴我就行。

現在測試完了。一切都成功了。

現在來看看第二個問題。合理使用異常。

又遇到一個問題。既然測試成功了,那總得給用戶一點交待吧?難道說,測試成功了,就悶聲大發財了?顯然不合適嘛。可以試想一下,你是程序員,然後點了個按鈕,測試。結果呢,實際上是測試成功了,但是系統啥動靜都不給你。然後你傻傻的等癡癡的盼,一直等到天荒地老……嗯嗯,扯的有點遠。如果你等一個小時還不見動靜,活不見人死不見屍的,你說你會不會罵娘?

那麼怎麼通知才能保證一定有效呢?println?這個不見得一定能看到。因爲別人也可能在同時輸出信息,一下就刷掉了。那麼有同學說了,最好是能暫停一下,我輸出以後,就暫停了,不動了。

嗯,很好。

大家想想看,輸出一大堆東西,然後此程序不動了,不繼續執行了,這是啥玩意?

這不就是異常嘛!

只有異常能保證程序員一定能看到這個信息,比如,測試成功。這就是爲什麼Spring要採用這種方式來通知的原因。

這裏呢,我想更正同學們一個習慣成自然的想法。異常不一定是通知壞消息的。異常就是異常,只要你願意,你甚至可以在代碼執行成功的時候,throws一個Exception。異常只不過是比較激烈的一種通知方式而已。無他,僅此而已。

現在又有個問題來了。既然要測試,而且每次執行到此處的時候都要測試一下。那麼……難道都卡在這裏不走了啊?顯然更不合適啊。

熟悉log4J的同學應該看出來了,這是log4J輸出的日誌。很明顯的,這種日誌只應當在開發期間存在,不應該在發佈期間存在。因爲開發期間數據庫變動很大,比如改表啊,改數據庫配置啊。所以需要通知用戶是否成功。但是產品一旦開發完畢,正式發佈,這種信息就不應再出現,因爲商業化運作的應用不允許亂動配置的,對不?

所以log4J提供了一種方法。消息級別。INFO的時候,是看不到這個異常的。實現起來也很好辦,catch了,然後不做任何處理,也就是空的catch塊。

具體實現的時候可以在catch裏判斷一下,如果等級是INFO的話,就不做任何事。如果不是,那就按照規則去做。

結合到 “合理使用異常” 這句話來說呢,就是說,需要拋出異常的時候,就拋出。不需要拋出的時候,就不拋出。對程序員來說,在必要的時候看到一串異常信息,是最合適的事情了。

關於異常的使用,這裏不展開說了。有興趣的同學可以參見林銳博士的 高質量Java編程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章