什么是 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://xxx.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 的配置,相应的修改负载均衡的配置

解决方案

网上能查询到的解决方案基本就是在 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。 上述方法,个人认为仅仅是解决了 nginx 记录 499 的问题,并没有从本质上解决客户端没能拿到请求响应的问题,具体还是得从超时时间的设定和程序处理性能的提升上从根本解决问题