nginx reload时对http keepalive连接的处理
nginx各协议的reload行为
| 协议 | 行为 |
| http keepalive | server fin |
| http2 | server fin |
| tcp | 保持shutdown不断 |
| udp | 保持shutdown不断 |
| grpc |
基于HTTP2与其保持一致,但是没实测 |
现象
nginx 在reload时,会对http keepalive的连接进行close操作。
抓包可以看见client收到了一个fin报文。

nginx 的worker也不会长时间留着shutdown状态,而是立刻退出。
4层的tcp监听上的连接,会使worker一直保留在shutdown状态。直到连接断开后,进程退出。
代码分析
一
见函数:ngx_worker_process_cycle,
所有idle的连接,会调用一次read的handler,然后将连接标记为close,最后在处理流程里根据这个标记,调用ngx_http_close_connection。关闭连接。


二
http keepalive过程
keepalive的连接会在每个request处理完的时候ngx_http_finalize_request调用连接关闭ngx_http_finalize_connection
在这个函数里,如果连接设了keepalive,就会进入 ngx_http_set_keepalive 函数

上述调用ngx_http_set_keepalive 函数的过程干三个事:
1 rev->handler = ngx_http_keepalive_handler;
2 c->idle = 1;
3 在2936行直接返回,跳过后面的connection close操作,所以连接还开着。
当 nginx reload的时候,2930或2931导致 ngx_http_set_keepalive 不会被调用,所以连接被关闭了,
抓包就看见了一个nginx到client的fin报文。
ngx_http_keepalive_handler
idle=1代表进程现在处于等待数据包读入状态。
ngx_http_keepalive_handler函数是数据包后的第一个handler,它的主要功能类似于 ngx_http_wait_request_handler 函数,
0 如果close=1,直接关闭连接。
1 设置idle = 0 (重新变成idle)
2 设置新的handler = ngx_http_process_request_line
request处理完成后,流程会重新走进ngx_http_finalize_request,最终判断是否进入ngx_http_set_keepalive
A 如果设置了ngx_exiting或者keepalive设置为0,将不会进入ngx_http_set_keepalive,直接将连接关闭。
B 否则再次进入ngx_http_set_keepalive 重新将idle设置为1,等待下一个请求的到来。
三
最后在函数ngx_worker_process_cycle,里。所有连接都关闭了,于是进程退出。

四
另外有个配置叫 worker_shutdown_timeout
https://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout
就是到时间之后,不管什么连接(包括tcp)全部断开进程退出,实现如下:将所有连接全部标记为close,然后再handler流程里close。

五
connection close的时候,会删掉read write timer,所有(三)里边的exit逻辑分支,再所有连接都断开了以后,就使进程退出了。

六
https://hg.nginx.org/nginx/rev/96ae8e57b3dd

这个是官方nginx的改动。主要是在nginx退出的时候,给keepalive的client回response时,带一个Connection:close。
基于(二)的代码分析,reload或exit发生时:
当connection处于idle时,nginx会直接将socket close掉。
然后client再发下一个response过来时,nginx的os回一个reset,client才知道失败了。(A)
当connection处于非idle(request处理过程中),且response还没发回时,会把response的Connection:keepalive改成Connection:close,紧接着socket close。
client的http层就会知道连接需要断开了。(B)
然后,进程退出。
另外,这个回复close的行为也就是情况(B),与 “keepalive_timeout到期,keepalive_requests满” 时的行为保持一致。
参考
https://github.com/alibaba/tengine/issues/1074
浙公网安备 33010602011771号