from: http://noops.me/?p=252
Haproxy作为MySQL中间层是很成熟的方案,特别是解决从库的负载均衡和故障切换,在生产环境中有着广泛的应用。
在实际使用过程中,有两个问题比较容易发生:
1. TCP端口耗尽
2. 网卡带宽跑满
本文重点讲讲如何优化问题1,问题2暂不讨论。
优化一: 使用尽可能多的端口
Linux系统默认提供了65K个端口,每当Haproxy建立了一个到MySQL的连接,就会消耗一个端口;当Haproxy断开和MySQL的连接时,该端口并不会立即释放,而是会处于TIME_WAIT状态(2*MSL),超时后才会释放此端口供新的连接使用。
我的环境中,tcp_fin_timeout为15秒,也就是说如果我环境中的haproxy可以承载的最大并发连接数为64K/(15*2)=2.1K,可实际上达不到这个上限,原因如下:
linux会保留一段端口,实际能参与分配的端口数只有50K,为了获得尽可能多的可分配端口,做如下调整:
记得修改/etc/sysctl.conf中对应的内容
优化二: 复用处于TIME_WAIT的端口
调整两个参数:
第一个参数很安全,可以不用过多关注。需要注意的是第二个参数,某些情况下会导致数据包被丢弃。
例如:client通过NAT连接haproxy,并且haproxy端打开了tcp_tw_recycle,同时saw_tstamp也没有关闭,当第一个连接建立并关闭后,此端口(句柄)处于TIME_WAIT状态,在2*MSL时间内又一个client(相同IP,如果打开了xfrm还要相同PORT)发一个syn包,此时linux内核就会认为这个数据包异常,从而丢掉这个包,并发送rst包.
不过通常情况下,client都是通过内网直接连接haproxy,所以可以认为tcp_tw_recycle是安全的,只是需要记住此坑。
优化三: 缩短TIME_WAIT时间
Linux系统默认MSL为60秒,也就是正常情况下,120秒后处于TIME_WAIT的端口(句柄)才会释放,可以将MSL的时间缩小,缩短端口的释放周期。
这是一个折中的数值,太小也会导致其它问题
优化四: 使用多IP
如优化一中所说,我们已经尽可能多的使用了系统提供的端口范围。但最多依然不超过65K。
Haproxy提供了内建的端口管理方法,可以充分利用以扩大我们的端口范围。
如果使用两个ip,我们可用的端口数就接近130K。扩展多个IP,就可以不断增加端口数。
优化五: 使用长连接
服务最好使用长连接,一是避免频繁的申请连接,导致端口耗尽;二是避免创建连接带来的时间消耗。
##评论
复用TIME_WAIT端口的坑我也遇到了。客服收到反馈,有少量用户访问游戏异常。最终定位到和TIME_WAIT有关。关闭timestamps机制可解决这个问题。
===========================================================================
关闭tw_recycle和tw_reuse可屏蔽NAT用户丢包的问题。但失去了快速回收的意义。
关于timestamps机制特性:
An additional mechanism could be added to the TCP, a per-hostcache of
the last timestamp received from any connection.This value could then be
used in the PAWS mechanism to rejectold duplicate segments from earlier
incarnations of theconnection, if the timestamp clock can be guaranteed
to haveticked at least once since the old connection was open. Thiswould
require that the TIME-WAIT delay plus the RTT togethermust be at least
one tick of the sender’s timestamp clock.Such an extension is not part
of the proposal of this RFC.
机制会缓存每个连接的最新时间戳。下一次收到的连接如果小于最新时间戳,则判
断为无效,丢弃数据。(副作用)
timestamps 和 tw_recycle 同时开启会有副作用产生。如关闭timestamps,则无
副作用产生了。
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1