java - Tomcat7 在工作一段时间后卡住

标签 java hibernate tomcat nginx tomcat7

我正在通过 Hibernate 4.3 和 JPA 2.1 使用 tomcat 7.0.52 servlets 服务器和 postgresql 数据库。

Nginx通过8080端口将所有对服务器的请求代理到tomcat服务器的8888端口。

服务器每秒大约有 200 个请求。几个小时后,它停止响应请求。我无法访问 tomcat7 管理器页面,无法访问 servlet 上下文。它总是响应请求超时错误。但服务器仍在工作,我的预定服务仍在工作并且可以访问数据库。

卡住时,我在 tomcat7 上的 CPU 使用率为 0.04-0.08%,在 postgresql 上的 CPU 使用率为 0.01-0.02%。正常工作时 tomcat7 上 3-4% 的 CPU 使用率和 postgresql 上 12-14% 的 CPU 使用率差异。

重启 tomcat7 服务器后,它再次正常工作。

我认为数据库没有问题,postgresql-9.3-main.log 是空的,但启用了日志记录。当我在 psql 中做错事时,我会看到它。

我认为 OutOfMemory 或任何其他异常都没有问题,因为在 tomcat7 catalina.out 和 localhost.YYYY-MM-DD.log 的所有日志文件中没有任何异常和错误。

我认为 nginx 没有问题,因为对其他端口和站点的所有请求都工作正常。

我认为内存泄漏没有问题,JAVA 总是消耗大约 700-800 MB 的内存并且卡住时间没有任何峰值。

我用谷歌搜索了很多类似的问题,但这对我没有任何帮助。

当我将 acceptorThreadCount 从 1 更改为 2 时,服务器卡住的速度要快得多。

似乎在接受 tomcat7 服务器的连接时卡住了。 我不知道我错过了什么。

JVM 选项:

JAVA_OPTS="-Xms1024m -Xmx2048m -XX:MaxPermSize=256m"

Tomcat7 版本信息:

Server version: Apache Tomcat/7.0.52 (Ubuntu)
Server built:   Jul 24 2014 08:38:51
Server number:  7.0.52.0
OS Name:        Linux
OS Version:     3.13.0-53-generic
Architecture:   amd64
JVM Version:    1.7.0_79-b14
JVM Vendor:     Oracle Corporation

Nginx 配置文件:

worker_rlimit_nofile 8192;
worker_processes 4;
timer_resolution 100ms;
worker_priority -5;

pid /run/nginx.pid;

events {
    worker_connections 2048;
    use epoll;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    output_buffers 2 512k;
    client_max_body_size 150M;

    gzip on;
    gzip_min_length 1100;
    gzip_buffers 64 8k; 
    gzip_comp_level 3;
    gzip_disable "msie6";
    gzip_http_version 1.1;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    keepalive_timeout 30;
    server_tokens off;
    reset_timedout_connection on;
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    types_hash_max_size 2048;
    server_names_hash_bucket_size 64;
    server_names_hash_max_size 2056;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
    include blockips.conf;
}

Nginx 服务器配置:

server {
    listen 8080;
    server_name <my_ip>;

    proxy_headers_hash_max_size 512;
    proxy_headers_hash_bucket_size 64;

    location / {
        proxy_set_header X-Forwarded-For $http_x_real_ip;
        #proxy_set_header X-NginX-Proxy true;

        proxy_pass         http://127.0.0.1:8888/; 
        proxy_redirect     off;
    }
}

连接器配置:

port="8888" 
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
acceptorThreadCount="1"
maxThreads="500"
URIEncoding="UTF-8"
redirectPort="8443"

提前谢谢你。

更新

问题解决了。我在这里找到了正确的解决方案 https://stackoverflow.com/a/3731978/7289901

hibernate 配置错误,因为 idle_test_periods 高于超时。将这些变量固定为正确的值后,服务器变得非常稳定。

更新 2

hibernate 的完整配置使我能够找出问题的原因:

<property name="hibernate.c3p0.acquire_increment">3</property>
<property name="hibernate.c3p0.acquireRetryAttempts">3</property>
<property name="hibernate.c3p0.acquireRetryDelay">250</property>
<property name="hibernate.c3p0.idle_test_period">10</property>
<property name="hibernate.c3p0.min_size">0</property>
<property name="hibernate.c3p0.max_size">50</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.timeout">30</property>
<property name="hibernate.c3p0.checkoutTimeout">500</property>
<property name="hibernate.c3p0.debugUnreturnedConnectionStackTraces">true</property>
<property name="hibernate.c3p0.unreturnedConnectionTimeout">30</property>
<property name="hibernate.c3p0.numHelperThreads">5</property>

最佳答案

我最初的猜测是您的 JPA 代码有问题。 开始时 Tomcat 和 DB 的 CPU 都较低,Tomcat 和数据库服务器上的 CPU 分别为 3-4% 和 12-14%。

如果您的 Tomcat 应用程序是无状态的,则扩展实际上是线性的,即使您将数据存储在 HttpSession 中,在您开始集群 Tomcat 之前也几乎没有开销。

数据库的扩展性也相当好,前提是您不执行全表扫描,但有适当的索引。也许您应该在 postgresql 上启用慢速查询日志记录(log_min_duration_statement 属性)以查看是否存在运行时间较长的个别查询。

如果无法连接到Tomcat 管理器,可能是因为使用了所有http 接受器。但是您应该仍然能够连接到 JVisualVM。 JViaualVM 有一个 CPU 采样器,如果你启动它,你应该能够看到时间花在了哪里。这里的一个问题是你不能只看 CPU 时间(因为大部分 CPU 都用在 DB 上),如果你看自己的时间,调用堆栈中的每个前面的步骤都会比你的代码高(而 tomcat 和 spring 通常添加 20ish stackframes)。

您可以尝试做一个线程转储,并检查 tomcats http 线程正在做什么(这基本上是 CPU 采样器所做的),这样您就可以看到它卡在哪里。

CPU 采样和线程转储应该让您了解应该在哪些方面集中精力。我的猜测是它与 JPA 相关。

用 JPA 编写代码可能会以非常糟糕的方式使用数据库。通常,延迟加载集合是一个很好的起点。如果你有一个 ER 模型 Company>-Employee>-phone (1-N, 1-N) 并且您想打印公司员工的所有电话号码,您可以从公司开始并循环遍历员工集合,并为每个员工循环遍历电话数字。这将导致 1 + N 查询,因为您需要一个查询来加载员工,并需要一个查询来加载每个员工的电话号码。更好的解决方案是使用 fetch join 查询来选择数据,这样数据库只执行一次查询,在一次操作中加载所有员工和电话号码。

另一个常见的错误是将数据添加到延迟加载的集合中,因为这会导致 JPA 首先加载集合中的所有数据。

由于您使用的是 Spring,您的实体管理器可能是托管的(和事务范围内的),因此您可能不会在持久性上下文中遇到数据积累问题。

如果您的查询是只读的,您应该检查您的 JPA 提供程序以查看是否有可以优化它的 @QueryHint。默认情况下,JPA 必须保留加载到持久性上下文中的每个对象的副本,因此它可以检查提交事务时是否进行了任何修改,此过程可能需要时间(并且对只读查询没有任何作用)。

您可以为 JPA 启用查询日志记录,但它往往会产生大量输出。

希望你能找到来源。

关于java - Tomcat7 在工作一段时间后卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41118651/

相关文章:

java - 如何在 XSLT 中使用 XPath 获取元素数组,包括缺失的元素?

java - 为什么 DragHandler exportAsDrag 禁用我的 MouseMotionListener?

java - 行程规划算法的图结构

Java hibernate获取所有实体

windows - Tomcat 中的虚拟目录不起作用

java - 尽管可以手动启动,但为什么 Tomcat 服务器不随 Eclipse 一起启动

java - 如何在没有缓存的情况下测量文件读取速度?

java - 在 java 中配置 hibernate 和 mysql 时出错

java - 任何持久更改的审计日志,不使用数据库触发器,而是使用 spring/hibernate

Tomcat 8.5 摘要式身份验证