tcp/http基于请求/响应式交互的上层协议服务器或反向代理服务一般有一个keepalive_requests参数可以指定一条tcp连接上最多能发送的请求数量,超过keepalive_requests数量时server端会关闭tcp连接,例如nginx的指令:
Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: http, server, location
This directive appeared in version 0.8.0.
Sets the maximum number of requests that can be served through one keep-alive connection. After the maximum number of requests are made, the connection is closed.
在使用这个指令做服务端时可能导致与其连接的client端数据丢失问题,最直接的现象就是client和server端请求数量不一致,即server端数据有缺失。一些真实的案例:多个反向代理服务器串接在一起提供服务器时,非最后一个代理服务器使用的keepalive长连接常有一些请求502状态码记录,在后端服务器上排查日志时找不到对应记录。
问题简要分析如下:
配置keepalive_requests=1000,即server端一条tcp长连接上收到第1000请求并处理返回响应时判断已经达到keepalive_requests数量,直接调用close()关闭连接,tcp交互序列描述如下:
示例:下边的抓包是一条长连接上多个请求传输有丢数据,server端配置了keepalive_requests=1000;使用过滤条件 tcp.stream eq 0 && tcp.len==14 过滤出1000个请求的响应报文如下:其中11.x.226.82是server端ip地址。
根据tcp协议原理可知上图红色框中部分属于server端的半关闭,即server端不再接收数据,但是不会影响client端仍接收传输链路上的数据,tcp协议交互还在链路上继续,
丢数据问题就发生在server关闭报文到达client端这段链路时间开销中;
上图可以看出因为client端收到第1000个response后还没有立即接收到FIN+ACK关闭报文,所以继续发送第1001个request(注意:应用层面调用send或write函数可以返回写成功),而1001 request到达server后tcp已经半关闭,不会再接收处理数据发送rst以通告对方。
因为client发送数据到tcp协议缓存即调用send或write函数返回写成功,但数据不能被server接收处理,导致发送数据丢失而应用程序没有感知,这种情况下最好有应用层保障机制(失败重传机制),即每个请求发送后都根据响应做判断数据送达。或者client端主动控制发送少于server规定keepalive_requests数量的请求。