在一次面试中被问到 HTTP 状态码 499 表示什么,我回答 499 表示客户端主动断开连接,是一个Nginx定义的非标准状态码。结果后半句被否定了。是我记错了吗?我当年可是经常和 499 打交道的。
HTTP/1.1标准的状态码
HTTP/1.1 标准(RFC 7231) 定义的状态码[1]https://datatracker.ietf.org/doc/html/rfc7231#section-6如下图所示,可以看到并没有 499。
IANA HTTP状态码注册表
The Internet Assigned Numbers Authority (IANA) maintains the official registry of HTTP status codes[2]https://en.wikipedia.org/wiki/List_of_HTTP_status_codes[3]https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.
可以看到, 499 是未分配的。
Nginx定义的状态码
那么,499 是怎么来的呢?
答案是, 499 Client closed request 是 Nginx 定义的,但是并不是只有 Nginx 一家定义了这个状态码。根据维基百科,有个叫 ArcGIS for Server 的软件也定义了这个状态码表示另一个意思:Code 499 indicates that a token is required but was not submitted[4]https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes.
Nginx 源码[5]https://github.com/nginx/nginx/blob/master/src/http/ngx_http_request.h可以明确的看到 Nginx 说 ”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"。
/* Our own HTTP codes */
/* The special code to close connection without any response */
#define NGX_HTTP_CLOSE 444
#define NGX_HTTP_NGINX_CODES 494
#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE 494
#define NGX_HTTPS_CERT_ERROR 495
#define NGX_HTTPS_NO_CERT 496
/*
* We use the special code for the plain HTTP requests that are sent to
* HTTPS port to distinguish it from 4XX in an error page redirection
*/
#define NGX_HTTP_TO_HTTPS 497
/* 498 is the canceled code for the requests with invalid host name */
/*
* 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
由此可见,我的回答没有太大问题,如果要更严谨一些,可以说,499 在 Nginx 里表示 客户端主动断开连接。
499的监控方法及意义
可以通过 Tengine 的 reqstat 模块来采集 499 状态码数据[6]https://tengine.taobao.org/document/http_reqstat.html。可以参考我之前的文章:基于http_reqstat和influxdb的Tengine监控方案。
499 表示客户端断开连接了。不断的按 F5 刷新浏览器就可以复现 499 状态码,因此出现 499 时不一定是服务端出了问题。但是如果服务是客户端程序的接口,客户端有超时重试机制时,比如 2s 未收到响应就断开连接重新尝试,这个时候 499 状态码大概率就是服务故障了,可能是服务过载,缓存失效等情况,常常还会伴随 504 错误,需要及时处理。
在我之前的实践中,5xx 状态码的报警是按 qps 触发的,比如持续 1 分钟出现大于 0.1 qps 的 5xx 错误,就认为需要报警。499 由于不一定是服务端问题,采用的是按比例触发,比如 499 状态码占所有请求的比重超过 10% 就报警。
更好的方法,可能是用 AIOps 的方法,通过异常检测来做 499 状态码的报警。
参考资料
↑1 | https://datatracker.ietf.org/doc/html/rfc7231#section-6 |
---|---|
↑2 | https://en.wikipedia.org/wiki/List_of_HTTP_status_codes |
↑3 | https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml |
↑4 | https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes |
↑5 | https://github.com/nginx/nginx/blob/master/src/http/ngx_http_request.h |
↑6 | https://tengine.taobao.org/document/http_reqstat.html |
知道该从何处得到知识也是重要的知识