天天看點

Tomcat性能問題調查

一、             程式現狀

        程式為基于Tomcat的WEB應用,在并發請求很少的情況下,程式運作正常,而當并發請求較多(70~300/30秒)時,WEB應用的頁面幾乎無法通路,通常需要重新整理多次才可能成功通路一次。

        通過Tomcat的status頁面,可以發現Tomcat 目前線程數已達配置檔案中設定的最大值(800個https,200個http),并且目前所有線程均處于忙碌狀态,大部分線程的生存期比較長,最長的可達20分鐘。

        觀察資料庫連接配接池,資料庫連接配接數量已達配置檔案設定最大值,但繁忙的資料庫連接配接并不多,大部分處于空閑狀态。

二、             調試與跟蹤面臨的問題

        多線程程式相對于單線程程式在跟蹤和調試方面要麻煩許多,特别是在當衆多線程啟動時才會發生的BUG,期望通過IDE進行調試是不可能完成的任務。此時,記錄log是最容易想到的跟蹤方式。

        但是在多線程中記錄log面臨的問題是由于線程衆多,記錄的log是也是由線程混雜生成的,因而很難從中抽取出一個線程的執行log進行分析。但是,要有效地分析線程運作情況,必須從繁雜的線程中抽取出一個線程的執行log。

        顯然,如果每一條log能夠有目前線程(更确切地說,是針對目前請求的響應)的惟一辨別,那麼抽取一個線程就成為可能。

三、             日志記錄解決方案

        要在log中辨別惟一線程,很容易想到的方式是在記錄log時同時記錄線程ID,但是對于Tomcat及類似的WEB的應用,使用線程ID存在以下兩個問題:

  1. Tomcat等伺服器會維護一個線程池,線程池中的線程會被反複使用,以便高效地響應請求,在這些種情況下,會有多個請求的log使用同一辨別,依然無法抽取針對一次請求響應的log。
  2. JDK1.4及之前并不支援線程ID,而目前面臨的應用正好使用的是JDK1.4.2。

利用線程類Thread提供的以下方法,可以實作對對每一請求進行惟一辨別:

        public static Thread currentThread()   // 取得目前線程對象

        public final void setName(String name) // 為線程設定一個名稱

        public final String getName()         // 取得線程的名稱

    具體實作過程如下:

  1. 在請求的入口處通過currentThread方法取得線程對象,然後調用其setName方法為線程設定一個惟一辨別,惟一辨別根據不同的應用可以設計不同的辨別,隻要其符合惟一性的标準即可。
  2. 在需要添加log的地方通過通過currentThread方法取得線程對象,然後調用getName方法取得目前線程的名稱,并将其記入目前log。
  3. 由于懷疑是某些操作耗時過長,是以在需要記錄log的方法中,入口處與出口處均需添加log。
  4. 更新程式後,取得log檔案即可抽取一個請求的執行流程進行分析。

四、             日志結果分析

通過日志,很快發現線上程中,以下方法被頻繁調用并且耗時較長:

synchronized  public  static  XXX  getInstance()

{

         if (m_instance == null)

         {

                   m_instance = new XXX();

         }

         return m_instance;

}

        該類是一個單執行個體類,通過分析其代碼,并沒有需要特别注意線程安全的地方,故可以直接取消方法getInstance的同步特性,隻是這樣可能線上程調用getInstance産生多餘一個的執行個體,但此後它會被Java進行垃圾回收,并沒有太大影響。或者,可以将同步範圍縮小至需要建立對象的代碼塊處,這樣将僅在程式剛開始運作時受到互斥的影響。

public  static  XXX  getInstance()

{

         if (m_instance == null)

         {

        Synchronized(this)

        {

            if (m_instance == null)

            {

                           m_instance = new XXX();

            }

        }

         }

         return m_instance;

}

五、             總結

  1. 多線程應用,取得能夠惟一辨別一個線程的log是迅速查找問題的關鍵,單純依據現象及經驗進行分析,可能會耗費大量的時間。前期在分析問題可能産生原因的同時,能取得有效log是一項優先度極高的工作。
  2. 單實列類的使用要慎重,其對空間占用率的影響可能遠不如因同步而帶來的負面影響。通常,應當隻将具有狀态的類設計成單執行個體類。
  3. 看起來似乎可以很快執行完畢的同步方法,線上程衆多、被頻繁調用時,也有可能是線程執行的瓶頸所在,不要将關注點隻放在哪些可能比較耗時的操作上。

歡迎通路夢斷酒醒的部落格

上一篇: POJ 1064
下一篇: poi1