2012-07-11 72 views
21

運行Tomcat(7.0.28)的我的Java Web應用程序定期變得無響應。我希望提供一些關於可能的罪魁禍首(同步?)的建議,以及一些推薦的工具,用於收集有關崩潰期間發生的事情的更多信息。我已經積累了一些事實:tomcat中的Java Web應用程序定期凍結

  • 當Web應用程序死機,Tomcat的繼續請求的線程送入應用程序,但應用程序不釋放他們。線程池填充到最大值(當前爲250),然後後續請求立即失敗。在正常操作期間,永遠不會有超過2或3個活動線程。

  • 有沒有錯誤或出現問題時,記錄到我們的任何Tomcat或web應用程序日誌的任何類型的異常。

  • 做經由Tomcat的管理Web應用程序我們的應用程序「停止」,然後在「開始」立即修復此問題(直到今天)。

  • 最近的頻率已非一日兩兩三次,雖然今天是差多了,大概20次,有時不能馬上恢復生機。

  • 問題在營業時間內

  • 我們的分期系統

  • 發生問題時不會發生的問題,在服務器上的處理器和內存使用率保持平坦(和相當低的)只發生。 Tomcat報告大量可用內存。

  • 當問題發生時,Tomcat會繼續響應。管理Web應用程序運行得非常好,並且tomcat繼續向我們的應用程序發送請求,直到池中的所有線程都被填充。

  • 發生問題時,我們的數據庫服務器保持響應。我們使用Spring框架進行數據訪問和注入。

  • 問題通常當使用率較高的發生,但有沒有在使用一個非常高的尖峯。

  • 問題的歷史:類似的事情發生在大約一年半前。在許多服務器配置和代碼更改之後,問題在大約一個月前消失。在過去的幾周裏,它發生得更爲頻繁,平均每天2-3次,有時甚至連續幾次。

  • 我今天可能不是線程安全的發現了一些服務器代碼,我把一個修復程序爲,但問題仍然發生(儘管不那麼頻繁)。這是非線程安全代碼可能導致的問題嗎?

UPDATE:隨着幾個職位提示數據庫連接池耗盡,我沒有在這個方向的一些搜索,發現這個其他Stackoverflow question這也解釋了幾乎所有我遇到的問題。

顯然,Apache的BasicDataSource實現中的maxActive和maxIdle連接的默認值每個都是8.此外,maxWait設置爲-1,因此當池耗盡並且連接的新請求進入時,它將等待永遠不會記錄任何異常。我仍然會等待這個問題再次發生,並在JVM上執行jstack轉儲,以便我可以分析這些信息,但看起來像是這個問題。它唯一不解釋的是爲什麼應用程序有時不能從這個問題中恢復。我想這些請求有時只是堆積起來,一旦落後,它永遠無法趕上。

UPDATE II:我崩潰期間跑了jstack,發現以下的約250(最大線程數):

"http-nio-443-exec-294" daemon prio=10 tid=0x00002aaabd4ed800 nid=0x5a5d in Object.wait() [0x00000000579e2000] 
    java.lang.Thread.State: WAITING (on object monitor) 
     at java.lang.Object.wait(Native Method) 
     at java.lang.Object.wait(Object.java:485) 
     at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118) 
     - locked <0x0000000743116b30> (a org.apache.commons.pool.impl.GenericObjectPool$Latch) 
     at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106) 
     at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) 
     at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) 
     at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) 
     at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:573) 
     at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:637) 
     at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:666) 
     at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:674) 
     at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:718) 

爲了我的未經訓練的人,這看起來相當確鑿。看起來數據庫連接池已經達到了上限。我在不修改maxActive和maxIdle的情況下配置了maxWait三秒,以確保在池填滿時我們開始查看記錄的異常情況。然後,我會將這些值設置爲適當的值並進行監控。

UPDATE III:配置MAXWAIT後,我開始看到這些在我的日誌,符合市場預期:

org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object 
     at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:114) 
     at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044) 
     at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) 
     at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) 

我已經設置maxActive爲-1(無限)和了maxidle到10.我會監視一段時間,但我的猜測是,這是問題的結束。

+7

'殺-3 '是你的朋友。運行這個並看看線程轉儲。您可能需要查看線程轉儲分析器以進行信息分組(http://java.net/projects/tda/)。 – mindas 2012-07-11 20:40:39

+0

沒有任何其他信息的Wild-guess(需要線程轉儲!):數據庫連接池耗盡。 – 2012-07-12 00:38:43

+1

這正是發生了什麼事對我來說,web應用變得太大,最大連接數過低,等待時間undifined,線程就一直堆放everynow然後和服務器將凍結。增加最大利弊,並設置最大等待的具體時間,現在我只是監測,但服務器運行良好。感謝這個迷你教程。 – Lauro182 2015-06-11 18:18:13

回答

12

從以往的經驗,你可能想看看你的數據庫連接池實施。這可能是因爲您的數據庫具有足夠的容量,但應用程序中的連接池僅限於少量連接。我記不起細節,但我似乎還記得有類似的問題,這是我轉向使用BoneCP的原因之一,我發現它在負載測試下非常快速和可靠。

嘗試以下建議的調試後,嘗試增加池中可用連接的數量,看看是否有任何影響。

我發現了一些服務器代碼,今天可能不是線程安全的, ,我把一個修復程序爲,但問題仍然發生 (儘管不那麼頻繁)。這是否是非線程安全代碼可能導致的問題 ?

這取決於你的線程安全的意思。這聽起來像你的應用程序正在導致線程deadlock。您可能需要運行的生產環境,以及配置的JVM,以允許調試器連接,然後用JVisualVM,JConsole的或其他分析工具(YourKit是優秀IMO)有在你有什麼的線程偷看,以及他們等着。

+0

是BoneCP的岩石。 commons-dbcp很糟糕。 Tomcat 7附帶一個dbcp https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html,但要使用它,驅動程序必須可以從tomcat-jdbc.jar使用相同的類加載器來訪問不想在每次安裝新的tomcat時將驅動程序jar放在tomcat lib目錄中。 – redochka 2013-01-18 00:13:18

+0

Redsonic:事實並非如此。如果您的'DataSource'在上下文中定義,而不是在上下文之間共享的JNDI資源,則它可以位於上下文的類路徑中(即在.war中)。這就是我在生產系統中使用它的方式。 – 2013-01-18 10:25:19

+0

啊好的。我沒有測試自己。我報告了tomcat 7文檔中的內容。那麼,在這種情況下,它也是一個選項:) – redochka 2013-01-18 15:31:42