Nginx和php-fpm:无法摆脱502和504错误

17

我有一台Ubuntu服务器和一个负载相当高的网站。该服务器:

  • 专用于Nginx,使用php-fpm(没有Apache),MySQL位于不同的机器上
  • 拥有8GB的内存
  • 每秒收到约2000个请求。

根据top命令,每个php-fpm进程消耗约65MB的内存:

top command

空闲内存:

admin@myserver:~$ free -m
             total       used       free     shared    buffers     cached
Mem:          7910       7156        753          0        284       2502
-/+ buffers/cache:       4369       3540
Swap:         8099          0       8099

问题

最近,我遇到了严重的性能问题。响应时间非常长,出现了很多网关超时,在晚上当负载高时,90%的用户只看到“服务器未找到”而不是网站(我似乎无法重现这个问题)


日志

我的Nginx错误日志中充满了以下消息:

2012/07/18 20:36:48 [error] 3451#0: *241904 upstream prematurely closed connection while reading response header from upstream, client: 178.49.30.245, server: example.net, request: request: "GET /readarticle/121430 HTTP/1.1", upstream: "fastcgi://127.0.0.1:9001", host: "example.net", referrer: "http://example.net/articles"

我已经尝试过切换到Unix套接字,但仍然遇到这些错误:

2012/07/18 19:27:30 [crit] 2275#0: *12334 connect() to unix:/tmp/fastcgi.sock failed (2: No such file or directory) while connecting to upstream, client: 84.
237.189.45, server: example.net, request: "GET /readarticle/121430 HTTP/1.1", upstream: "fastcgi://unix:/tmp/fastcgi.sock:", host: "example.net", referrer: "http
://example.net/articles"

同时,php-fpm 日志中充满了这些内容:

[18-Jul-2012 19:23:34] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there  are 0 idle, and 75 total children

我试过将给定的参数增加到100,但似乎仍不足。


配置

这是我的当前配置。

php-fpm

listen = 127.0.0.1:9001
listen.backlog = 4096
pm = dynamic
pm.max_children = 130
pm.start_servers = 40
pm.min_spare_servers = 10
pm.max_spare_servers = 40
pm.max_requests = 100

nginx

worker_processes  4;
worker_rlimit_nofile 8192;
worker_priority 0;
worker_cpu_affinity 0001 0010 0100 1000;

error_log  /var/log/nginx_errors.log;

events {
    multi_accept off;
    worker_connections  4096;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    access_log off;
    sendfile        on;
    keepalive_timeout  65;
    gzip  on;

    # fastcgi parameters
    fastcgi_connect_timeout 120;
    fastcgi_send_timeout 180;
    fastcgi_read_timeout 1000;
    fastcgi_buffer_size 128k;
    fastcgi_buffers 4 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_intercept_errors on;

    client_max_body_size 128M;

    server {
        server_name example.net;
        root /var/www/example/httpdocs;
        index index.php;
        charset utf-8;
        error_log /var/www/example/nginx_error.log;

        error_page 502 504 = /gateway_timeout.html;

        # rewrite rule
        location / {
            if (!-e $request_filename) {
                rewrite ^(.*)$ /index.php?path=$1 last;
            }
        }
        location ~* \.php {
            fastcgi_pass 127.0.0.1:9001;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_script_name;
            include fastcgi_params;
        }
    }
}

我将非常感激任何关于如何确定问题以及可以调整哪些参数来解决此问题的建议。或者,也许8GB的RAM对于这种负载来说还不足够?


我不太确定你的设置细节,但你可能想要计算一下你可能会消耗多少内存。一个快速的猜测是,你的130个子进程每个需要65MB,总共需要8.5GB(没有考虑1000/1024问题,也没有计算其他进程)。当然,我建议首先检查你是否有足够的内存来运行所有这些子进程和其他进程。 - Nanne
1
很可能是MySQL访问中阻塞了php-fpm进程。 - VBart
1
顺便提一下,它们每个只使用了12 MB的物理内存。你应该查看RES。 - VBart
3
你应该检查一下你的MySQL服务器。很可能它负载过重或者限制了并行MySQL连接的数量。你需要找到瓶颈所在。根据你提供的top截图,似乎不是RAM或CPU的问题,因此最有可能是I/O问题。 - VBart
@VBart,谢谢。你是对的——1)mysql服务器是问题的原因2)我看错了“top”中的列,并因此设置了严格的限制。如果你把这个作为答案发表,我会很乐意接受它。 - Silver Light
显示剩余2条评论
5个回答

1
有一些问题。这样一个繁忙的网站还值得去解决它们。目前MySQL可能是根本原因。但长期来看,你需要做更多的工作。
缓存
我看到你的错误消息中显示了一个向php上游发出的get请求。这在如此高流量的网站(如你所提到的2000r/s)看起来不太好。这个页面(/readarticle/121430)似乎是一个完全可缓存的页面。首先,你可以使用nginx来缓存这样的页面。查看fastcgi cache
GET /readarticle/121430

php-fpm

pm.max_requests = 100

该值表示在服务100个请求后,php-fpm主进程将终止一个进程。php-fpm使用此值来抵御第三方内存泄漏。您的网站非常繁忙,每秒达到2000个请求。您的最大子进程数为130,每个进程最多只能提供100个请求。这意味着在13000/2000=6.5秒后,它们将全部被回收。这太多了(每秒杀死20个进程)。您应该至少从1000开始,并增加该数字,只要您不看到内存泄漏。有人在生产中使用了10000。

nginx.conf

  • 问题1:

        if (!-e $request_filename) {
            rewrite ^(.*)$ /index.php?path=$1 last;
        }
    

    应该替换为更有效的try_files:

        try_files $uri /index.php?path=$uri;
    

如果使用位置块和正则重写规则匹配,您可以节省额外的if

  • 问题2:使用unix套接字比使用ip更节省时间(根据我的经验约为10-20%)。这就是为什么php-fpm将其作为默认值的原因。

  • 问题3:您可能有兴趣在nginx和php-fpm之间设置保持活动连接。 在nginx官方网站这里提供了一个示例。


+1指的是try_files。如果我理解正确的话,问题2可以使用Unix套接字而不是TCP来解决。我使用与@Silver Light非常相似的设置,并且使用Unix套接字、php-fpm和apc,我可以在WordPress上达到20万用户。 - spinus

1
我需要查看您的php.ini设置,我认为这与MySQL无关,因为您似乎遇到了套接字错误。此外,这是在一段时间后开始发生的问题还是服务器重新启动时立即发生的问题?
尝试重新启动php5-fpm守护进程,并在尾随错误日志时观察发生了什么。
检查您的php.ini文件以及通常位于/etc/nginx/fastcgi_params中的所有fastcgi_params。有很多示例可以帮助您完成所需操作。
另外,您是否启用了apc php缓存扩展?
如果您使用lamp堆栈,则在php.ini文件中将如下所示:

extension=apc.so
....
apc.enabled=0
还可以从命令行执行一些mysql连接负载测试,看看结果如何。

APC是非常好的东西。它帮助我很多,让网站保持快速(特别是WordPress)。 - spinus

1

0

为了回答这个问题:

您应该检查您的MySQL服务器。可能它已经超载或限制了并行MySQL连接的数量。您应该找到瓶颈。根据您的顶部截图,它看起来不像是RAM或CPU,那么最有可能是I/O。 - @VBrat

未来您可能想要做的事情:

1- 增加您的RAM大小。

2- 使用缓存。请参阅this article以了解如何加速您的网站。

3- 减少执行的查询数量。


0
  • 设置 PHP 的 APC 扩展(检查/配置)
  • MySQL - 检查配置、索引、慢查询
  • 安装和配置 Varnish。它可以缓存页面请求,对于减少需要进行的 php 请求和 mysql 查询非常有用。它在处理 cookies/ssl 时可能会有些棘手,但除此之外并不太困难,而且非常值得运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接