我们在 Jetty 下运行的 java servlet 遇到了一个非常棘手的问题。它在负载低到中等时运行良好,但当负载达到一定水平时,它将在大约 10-20 分钟后停止响应请求。
如果我们在使用 curl 连接到主 java 进程时跟踪它,我们可以看到它建立连接、接收请求、解析它并执行它通常所做的事情(查询 Solr 服务器、执行一些 MySQL 查询等),但是结果永远不会发送回客户端。
当它挂起时,它将无限期地挂起。再多的时间都不会让它“跳出来”,但是如果我们在主 java/jetty 进程下杀死任何随机线程,线程数就会下降,它会再次开始响应请求。
这是它的外观:
# curl http://localhost:8080/some-servlet-url
(Does not respond at this point)
# ps -efL | grep qserv | wc -l
243
# ps -efL | grep qserv | wc -l
243
# ps -efL | grep qserv | wc -l
243
(Number of threads remain seemingly constant)
# kill 29760 <--- random thread under the main java/jetty process
# ps -efL | grep qserv | wc -l
26
(Number of threads immediately decreases sharply)
# curl http://localhost:8080/some-servlet-url
... HTTP response ...
(Responds to connections again)
该服务器是一个运行 Ubuntu 12.04.1 LTS 和 Jetty 8.1.7.v20120910 的 m2.2xlarge Amazon EC2 实例
$ java -version
java version "1.7.0_07"
OpenJDK Runtime Environment (IcedTea7 2.3.2) (7u7-2.3.2-1ubuntu0.12.04.1)
OpenJDK 64-Bit Server VM (build 23.2-b09, mixed mode)
我们尝试了几种不同的 java 和 Jetty 版本。我们也尝试过使用 Tomcat 而不是 Jetty——同样的问题。
使用 YourKit 分析应用程序显示没有明显的线程锁定或过多的 CPU 使用率。
有任何想法吗?
编辑:我们能够获得挂起的 java 进程的堆栈跟踪,看起来所有的 http 线程都处于这种状态:
"http-bio-8080-exec-5" daemon prio=10 tid=0x00007fe518007800 nid=0x1fc5 in Object.wait() [0x00007fe57934f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ee9d230> (a org.apache.commons.pool.impl.GenericObjectPool)
at java.lang.Object.wait(Object.java:503)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:810)
- locked <0x000000076ee9d230> (a org.apache.commons.pool.impl.GenericObjectPool)
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:95)
at net.acmecorp.active.QueryResultXMLFormatter.selectBestHitsAndRunDocumentCompletion(QueryResultXMLFormatter.java:362)
at net.acmecorp.active.QueryResultXMLFormatter.queryResultToXMLRootElement(QueryResultXMLFormatter.java:167)
at net.acmecorp.active.QueryPrepareAndExecuter.prepareParametersAndExecuteQuery_AndInvokeFormatter(QueryPrepareAndExecuter.java:239)
at net.acmecorp.servlets.MultiQueryServlet.handle(MultiQueryServlet.java:470)
at net.acmecorp.servlets.MultiQueryServlet.doGet(MultiQueryServlet.java:85)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
- locked <0x0000000700dd70d0> (a org.apache.tomcat.util.net.SocketWrapper)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Locked ownable synchronizers:
- <0x000000076ed40990> (a java.util.concurrent.ThreadPoolExecutor$Worker)
我自己并不是一个真正的 Java 人,所以我无法确切地看到这里出了什么问题,但看起来他们都在等待什么......