最近有七层负载均衡器在大量流量的情况下出现异常,表现为time_wait数量过多,客户端请求失败,作为负载均衡器,对于RS来说,就是一个客户端,通常客户端会起一个随机端口一RS的server连接,这时就不得不考虑负载均衡器端口数量受限问题,为了能尽量缕清这个端口数量和qps的问题,有了以下的测试。
1.png

测试

测试场景:

  • 系统环境参数:
    2.png

net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_recycle = 0
net.ipv4.ip_local_port_range = 35535 65535
net.ipv4.ip_local_reserved_ports = 2090,3060,3080,8060,8080-8082,9000,9080,9090,10029,22223-22323
net.ipv4.tcp_tw_reuse = 1

  • ab模拟客户端发起请求qps约1000上下(有超过1000的):
    3.png
  • ab测试结果,有部分失败:
    4.png
  • nginx日志告警端口耗尽:
    5.png

nginx报警端口耗尽时间出现在:2019/03/03 15:42:33

  • tcp端口状态:
    通过每秒输出ss -s查看,nginx报错时的端口数量,基本在15:42:33时端口数量到达系统设置的上限30000:

6.png

  • 但是系统日志message并没有记录端口耗尽告警相关日志:
    7.png
  • 调整ab请求qps为900(低于1000):
    8.png
  • 查看nginx无报错:
    9.png
  • 调整快速回收参数
    net.ipv4.tcp_tw_recycle = 1

同样的端口数范围以及1000qps上下的qps请求条件,测试无异常结果:
10.png

11.png

但是,开启tcp_tw_recycle的话,会有一个时间戳的检验,这时会有一个副作用,就是当客户端的时间不一致时,会有数据包丢弃的风险(开启后会缓存连接的时间戳,60秒内,同一源IP的后续请求的时间戳小于缓存中的时间戳,内核就会丢弃该请求。)

  • 增加一个RS测试:
  • 大于2000qps时:
    12.png

出现临时端口耗尽报错:
13.png
14.png
15.png

  • 低于2000qps时,结果正常:
    16.png

17.png

结论

以上测试示例的结果可以大致得出,作为负载均衡反向代理,没开启net.ipv4.tcp_tw_recycle = 1参数,单个负载均衡器的处理的短连接的qps上限约为net.ipv4.ip_local_port_range/(net.ipv4.tcp_fin_timeout*2)*RS,如上述实例
:单个proxy和rs时:qps=(65535-35535)/(15*2)=1000,单个proxy和两个rs时:qps=(65535-35535)/(15*2)=2000,单个负载均衡器处理超过这个qps时会有端口耗尽从而导致服务异常的风险,但是开启了快速回收,会有时间戳的副作用,应更加实际情况平衡好。

解决方案

根据上面的测试结论,在一个四元组(五元组)作为一个唯一表示套接字中,在反代的场景下,作为负载均衡器,目的IP地址:目的端口已经固定,所以在源IP和源端口上进行:

  • 增加负载均衡器数量或者在负载均衡器上增加网卡ip,以此来增加四元组的个数
  • 增加端口使用范围
  • 增加后端服务器数量
  • 缩短MSL的Time_Wait回收时间
  • 开启快速回收参数
  • 使用长连接

延伸

上面测试讨论的负载均衡器作为客户端反代流量到后端RS时,一般出现在常用的nginx、haproxy等作用在用户空间的反代程序上,但是理论上lvs并不会出现这个问题,因为lvs只是纯粹的流量转发,处理在内核链表上完成,不管是DR和是TUN模式,最终到RS拆解接收的请求报文中,sourceip:port都是真实的客户端ip端口,而不是lvs的ip和端口,所以lvs并不会受到临时端口耗尽的问题,这也是为什么多数情况下,出入口都是先lvs再到后面的nginx或haproxy再调度,但是lvs作为4层反代不能根据实际的业务需求处理七层的URL个性转发。