公司一项目采用LNMP架构,最近老是报502,Nginx错误日志如下:
代码语言:javascript复制[error] 7649#0: *60873458 upstream sent too big header while reading response header from upstream, client: XXX, server: XXX, request: "GET /xx HTTP/1.1",
因为涉及到一些敏感信息,所以一些关键信息去除了,但这不影响问题的分析。
从字面理解应该是Upstream返回的header头超出限制了 ,这里大概脑补下FastCgi协议,Nginx和PhpFpm是通过这个协议进行数据传输的,其中Nginx和后端所有Upstream交互都是分两步的,第一步是处理头,第二步是处理body,每个协议实现自己的部分。
FastCgi协议这里不详述,在本案例中,头部分相关于后端写回的http头太大了,具体是哪部分大,一般来说是Cookiem的内容过长。
百度了一下,说要调整缓冲区大小,
代码语言:javascript复制fastcgi_buffer_size 16k;
fastcgi_buffers 8 128k;
其中fastcgi_buffer_size默认是4K,在这个案例中,因为后端返回的Cookie和其它Http头信息超出4K了,所以报错,这里调整为16K,问题解决。
具体可以看下Nginx源码中处理FastCgi头的函数ngx_http_upstream_process_header:
代码语言:javascript复制 if (rc == NGX_AGAIN) {
if (u->buffer.last == u->buffer.end) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"upstream sent too big header");
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); return;
} continue;
}
这里会判断last是否等于end,其中u->buffer为Nginx和后端Upstream交互的缓冲区,Nginx通过ngx_buf结构来管理缓冲区:
代码语言:javascript复制struct ngx_buf_s {
u_char *pos; u_char *last; off_t file_pos;
off_t file_last;
u_char *start; /* start of buffer */
u_char *end; /* end of buffer */
ngx_buf_tag_t tag;
ngx_file_t *file;
ngx_buf_t *shadow;
……
其中start是缓冲的开始部分,end是结束部分,这是系统分配下来的,实际用的时候是将数据读到缓冲区里,并增加last部分,还有个pos表示应用处理的部分。
打个比方,我申请了1000的内存,开始地址为 1000,结束为1999(当然实际地址不可能这么小,这些是留给OS的,这里只是便于说明问题),那刚开始start为1000,end为1999,pos和last都为1000;这个时候应用读取后端upstream的头部分,读取了500字节,那last指向1500,post指向0,然后解析upstream的头,转换为内部一些结构,则pos也推进了。
回到上面的情况, 这里判断了last是否指向了end,这种情况下表示这个缓冲区已经用完了,在案例中,因为配的是4K,即表示从upstream中读取的头超出了4K,所以报错。
这个配置是针对每个Upstream的,即如果同时有1000个请求,则占用1000*16K的内存,所以不宜设的过大,这也是Nginx保存内存的一种办法。
Nginx配置不当险酿S0
Skywalking Php注册不上问题排查