负载均衡nginx中的FIN包传递

发布于:2025-04-17 ⋅ 阅读:(25) ⋅ 点赞:(0)

序言

    流言止于智者,不过也看怎么个智慧法。

    现实情况大多是请君入瓮,开始的时候吹的天花乱坠,等到真正进去了才能认识到水的深浅。

FIN包的传递

    1 背景

    在使用负载均衡的时候,一般都是用反向代理的nginx来实现,突然报一个故障,说是nginx的后端主动关闭了连接,导致后端向nginx发送响应的时候,出现了报错connection closed。架构图如下:

    2 查日志

    一般不知道咋处理的时候,就先看下日志,查了一下对应时间的access log和error log,发现日志里面记录的一切正常,后端返回的200响应,而nginx返回给客户端也是200响应,看起来感觉没啥问题。

    查看error log的时候,发现里面空空如也,原来碰到的问题都是后端关闭了连接,所以在error log里面经常能看到

upstream prematurely closed connection while reading response header from upstream

    只看到别人关闭连接导致出错,而自己关闭连接的情况很少碰到,那么问题来了,在什么时候nginx会主动关闭连接?一般只有对应的一些超时才会主动的关闭连接,而且是空闲连接,并且后端还开启了连接池。

    3 抓包

    在不能解决问题的时候,就只能抓包了,抓包的主要目的是看谁先发送的FIN包,也就是在一切正常请求的时候,FIN谁先发出,就是谁有问题了。

    抓包的时候,一般先问问发生的频率,如果发生的频率低,那么抓包的时间就长了,而且如果qps大,那么抓包文件就更大了,一般使用循环抓包,例如一个文件200m,抓20个文件循环抓,这样不至于让磁盘满。

    抓包之后发现的结论就是:当nginx对后端发送了请求之后,立即发送了FIN包进行关闭了连接(nginx和后端开启了keepalive连接);客户端发送请求给nginx之后,立即发送了FIN包给nginx;从而得到是客户端发送了关闭请求,从而导致连接的断开,这种情况一般就是客户端刚打开一个页面,咔关电脑,关闭了连接。

    4 问题点

    在上面抓包之后,出现几个疑问:

    Q1:FIN包会传递吗,无论是四层还是七层?

    Q2:对于长连接的请求,客户端发送了FIN包,长连接是可以复用的,也会关闭请求吗?

    Q3:为什么日志记录的是200响应,明明没有抓到后端的响应,后端发送的时候都报错了,日志记录的应该是499?

   A1:在四层之中,不存在http协议复用的问题,所以FIN包会直接传递,当FIN包到了之后,就会直接透传tcp的FIN到后端服务中,从而进行关闭连接。

    在七层http,是可以复用连接的,从而当客户端发送FIN的时候,在默认情况下,是会将FIN包发送到后端,从而关闭此长连接,而连接池中其他连接不受影响,也就是此时日志会记录为499的响应,表示客户端中断了连接;而当nginx的配置文件中,

proxy_ignore_client_abort on;(忽略客户端的意外关闭,继续处理请求)

    也就是当客户端发送了FIN或者RESET的时候,服务端继续处理,这种其实比较耗费性能,但是可以大大减少请求中的499响应,开启或者不开启,如果做了对应的监控,就会发现如果开启了,会减少499的数量,否则会有大量告警。

    A2:答案和上面一致,主要看nginx的配置参数有没有修改,默认值就会直接透传关闭连接。

    A3:为什么日志记录的不是499而是200,从抓包来看,未看到后端的响应包过来,从而猜测可能是后端已经发送了对应header过来,也就是响应码等其他数据,然后记录了对应的日志,而后端在发送body的时候,进行了一个报错。

    5 如果发送FIN失败,会如何?

    在进行关闭连接的时候,如果nginx给客户端发送FIN的时候失败,你觉得会是多久这个连接关闭?

    这个时候,一般最能想到的就是内核参数tcp_retries2参数,这个在内核中默认值是15,以为能传递15次,但是这个时间就很长了,可能到十几分钟,一直在重传FIN包。

    但是如果你抓包的话,会发现大概重传9次就结束了,因为还有一个内核参数来控制这个时间,也就是tcp_fin_timeout,大概是60秒,从而9次重传结束。

    6 nginx的server name

    另外说个小问题,在nginx的server_name的配置中,一般都是配置域名什么的,但是大部分都是小写,因为对于nginx来说,接收到了host头部就会把对应的值转化为小写进行和nginx的进行比较。

   当一个nginx上面配置几百个的时候,如果你要查询一个配置文件,分散在vhost中,那么只能用grep来查,但是就是没查到。。。最后发现,server_name居然是混合大小写,虽然没有意义,但是却给排查挡了路。不同的人维护,从而产生不同的结果。

    以后排查的时候,还是直接grep -ir吧,忽略大小写,进行递归查询。

风言风语

    还是四层比较简单,说断开连接就直接全部断开连接,客户端的连接断开,后端的连接断开。而对于七层来说,因为连接可以复用,从而可以应对客户端的意外断开连接,有一定的灵活性,不同的协议,处理了不同的问题。

   客户端的长连接参数在nginx中,没想到还能配置在server段和location段,还以为只能配置在http段呢。

   运维的价值是什么?无法计算的价值。。。杂活谁来干,如果连运维都不干,那估计就没人干了。


网站公告

今日签到

点亮在社区的每一天
去签到