nginx 499 产生的原因

什么是 nginx 的 499

499 是 nginx 扩展的 4xx 错误,目的只是用于记录,并没有实际的响应。
看一下 nginx 源码 ngx_http_request.h 对 499 的定义:

* HTTP does not define the code for the case when a client closed * the connection while we are processing its request so we introduce * own code to log such situation when a client has closed the connection * before we even try to send the HTTP header to it #define NGX_HTTP_CLIENT_CLOSED_REQUEST 499

由上述表述可知,nginx 499 代表客户端请求还未返回时,客户端主动断开连接。

什么情况下 nginx 记录 499

通过网上查询相关资料学习与了解,自己总结大致原因就是请求在指定的时间内没能拿到响应而关闭了连接。问题症结点为两处:1、指定的时间;2、程序处理的性能。

最开始时,表述过 nginx 499 是客户端主动断开了连接。这里的客户端概念,我的理解是对请求连接过程中的下游服务而言的,例如浏览器与 nginx 之间的连接,浏览器为客户端;nginx 与其分发的服务而言,nginx 是客户端;php 处理程序中发起的 curl 请求而言,php-fpm 可视为客户端。
上述的指定时间内的这个时间,一般是定义的处理超时时间,可能的原因就是这个 时间设短 了。
以发起 curl 请求为例,数据传输的最大允许时间用 -m 参数来指定。
curl -m 20 "http://somewebsite.com"
数据传输的最大允许时间超时的话,curl 断开了请求,而 web 服务器如 nginx 还在处理的话,则 nginx 会记录 499;

再如 nginx 作为反向代理时,nginx 将请求分发至对应的处理服务器时,有两对超时参数的设置: proxy_send_timeout proxy_read_timeout ; fastcgi_send_timeout fastcgi_read_timeout
两对参数分别对应的是 ngx_http_proxy_module 和 ngx_http_fastcgi_module 模块的参数。两对参数默认的超时时间都是 60 s。在 nginx 出现 499 的情况下,可以结合请求断开前的耗时和这两对设定的时间进行对比,看一下是不是在 proxy_pass 或者 fastcgi_pass 处理时,设置的超时时间短了。

再如 php 操作超时。打开 php.ini 查看 max_execution_time max_input_time 两个参数。两者分别是 php 程序执行的最长时间和表单提交的最长时间。

如果后端服务中访问了负载均衡的,例如 AWS 上的 Elastic Load Balances 等。出现 nginx 上设置的超时很大,nginx 同样记录了 499 状态,那么有可能就是负载均衡在默认时间(一般是 60 s)后删除了连接。这种情况下,可根据 nginx 的配置,相应的修改负载均衡的配置。

性能问题就比较宽泛了,不太便于排除,可能会有的情况:
1、CPU 和内存的使用情况
linux 上,可以用 top 命令查看 CPU 和内存的使用情况。

top - 16:59:03 up 334 days, 23:10,  1 user,  load average: 0.06, 0.08, 0.07
Tasks: 114 total,   2 running, 112 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.5 us,  0.3 sy,  0.0 ni, 98.5 id,  0.7 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2916192 total,   516184 free,  1540972 used,   859036 buff/cache
KiB Swap:        0 total,        0 free,        0 used.   999988 avail Mem 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                     
11475 work      20   0  157680   2188   1520 R   0.7  0.1   0:00.34 top                                                                         
12206 elastic+  20   0 3685272 980.1m    272 S   0.7 34.4   1027:58 java                                                                        
24451 mysql     20   0 1745372 275176   2892 S   0.3  9.4 761:20.55 mysqld                                                                      
    1 root      20   0  232328  46328   1324 S   0.0  1.6  33:03.32 systemd                                                                     
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.80 kthreadd                                                                    
    3 root      20   0       0      0      0 S   0.0  0.0   0:37.02 ksoftirqd/0                                                                 
    5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H                                                                
    7 root      rt   0       0      0      0 S   0.0  0.0   0:20.47 migration/0                                                                 
    8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh                                                                      
    9 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcuob/0                                                                     
   10 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcuob/1                                                                     
   11 root      20   0       0      0      0 R   0.0  0.0 126:24.86 rcu_sched

第三行,cpu状态信息,具体属性说明如下:

0.5% us — 用户空间占用CPU的百分比;
0.3% sy — 内核空间占用CPU的百分比;
0.0% ni — 改变过优先级的进程占用CPU的百分比;
98.5% id — 空闲CPU百分比;
0.7% wa — IO等待占用CPU的百分比;
0.0% hi — 硬中断(Hardware IRQ)占用CPU的百分比;
0.0% si — 软中断(Software Interrupts)占用CPU的百分比。

第四行,内存状态,具体信息如下:

2916192k total — 物理内存总量(2.9GB);
1540972k used — 使用中的内存总量(1.5GB);
516184k free — 空闲内存总量(0.5GB);
859036k buffers — 缓存的内存量 (859M)。

2、php-fpm 等 fastcgi 处理程序进程数不够用
需要理解的参数有: pm、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers。
pm 表示进程数的控制方式,分别为 static (静态)和 dynamic (动态)。

pm = dynamic 如何控制子进程,选项有static和dynamic
pm.max_children:静态方式下开启的php-fpm进程数量
pm.max_requests:php-fpm子进程能处理的最大请求数
pm.start_servers:动态方式下的起始php-fpm进程数量
pm.min_spare_servers:动态方式下的最小php-fpm进程数
pm.max_spare_servers:动态方式下的最大php-fpm进程数量

3、mysql 等数据查询过程缓慢
MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10S以上的语句。默认情况下,Mysql数据库并不启动慢查询日志,需要我们手动来设置这个参数。具体可参考Mysql 慢查询日志总结

4、程序处理逻辑性能不好
这一项需要查询程序的逻辑处理是否有过多的重复的或者冗余的过程了。:smile:

他人给的解决方案

网上能查询到的解决方案基本就是在 nginx.conf 的 http 块中添加 proxy_ignore_client_abort on;
默认的情况下该参数是关闭的。nginx 官网给的参数说明如下:

Determines whether the connection with a proxied server should be closed when a client closes the connection without waiting for a response.

主要意思就是在客户端主动关闭连接后, nginx 与分发服务器的连接是否保持连接。
如果使用了 proxy_ignore_client_abort on; Nginx 会等待后端处理完(或者超时),然后记录「后端的返回信息」到日志。所以,如果后端返回 200,就记录 200 ;如果后端放回 5XX ,那么就记录 5XX 。
如果超时(默认60s,可以用 proxy_read_timeout 设置),Nginx 会主动断开连接,记录 504。