最近由于项目需要,在研究打压测试工具,以及当测试连接过多后端服务器配置问题
测试工具选用locust,locust中文意思为蝗虫,可以想象,locust就像成片的蝗虫,扑向我们的服务。
它支持分布式的打压测试,每个实例可自定义执行任务,执行任务可用python脚本实现,具体如何写python脚本这里就不详细介绍了,网上能搜到很多相关资料,这次的文章主要着重介绍如何实现我们的需求以及对遇到的问题分析。遇到的问题主要是两个:如何在每次执行任务都使用新的连接,大量连接时如何处理大量出现的TIME_WAIT连接
首先,我的测试场景是,大量的客户端会间歇性的请求服务器,并不会长时间连接,基本可以理解为每次请求都是短连接,每条连接发起一次请求后就会断开。所以首先需要解决的问题是,如何使每次执行任务都是用新的连接。
locust默认使用的是requests库,创建的会话默认会使用长连接,会复用连接,一条发起多次请求,这并不符合我的要求,所以在每次请求完数据后,我需要自己关闭连接。
关闭连接的手段有很多:
a、断开整个会话的连接
self.client.close()
b、断开客户端连接
r = self.client.request("post", "/api/query", data=json.dumps(payload), verify=False)
r.connection.close()
c、请求时带着Connection: close头部,让服务器断开连接(在HTTP1.1协议中,Connection头部有两个值,close和keep-alive,这个头就相当于客户端告诉服务端,服务端你执行完成请求之后,是关闭连接还是保持连接,保持连接就意味着在保持连接期间,只能由客户端主动断开连接)
r = self.client.request("post", "/api/query", headers={'Connection':'close'}, data=json.dumps(payload), verify=False)
不论a、b还是c,都能实现每次请求后连接断开的目的,但是引发的副作用却不一样。
因为tcp连接有一种TIME_WAIT状态,连接的主动关闭方在发送四次挥手的最后一个ACK后会变为TIME_WAIT状态,保留此状态的时间为两个MSL(linux里一个MSL为30s,是不可配置的)
仔细的同学可能已经发现了这里的区别,a和b方法是客户端断开连接,c是服务器断开连接,这也就决定了TIME_WAIT会在哪一端出现(前面已经说过,连接的主动关闭方会变为TIME_WAIT状态)。
抓包验证发现:
a、b方法的FIN报文都是由客户端发起的
c方法的FIN报文由服务器发起
如果由服务器断开连接,会导致服务器端产生大量TIME_WAIT状态的连接,这个问题直接的影响就是服务器的端口很快会被耗尽,导致客户端无法与服务器成功建立新的连接
而如果由客户端断开连接,经测试,测试机端很快出现上万的TIME_WAIT状态的连接,测试量根本打不上去,大量连接建立失败
所以我们需要调整系统的配置,来优化tcp连接的处理
如果由客户端关闭连接,需要修改的配置如下:
vi /etc/sysctl.conf
net.ipv4.tcp_timestamps=1 开启后下面的tw参数才能生效
net.ipv4.tcp_tw_reuse=1 开启重用,允许将TIME_WAIT重用与新的连接
net.ipv4.tcp_fin_timeout = 30 缩短TIME_WAIT_2到TIME_WAIT的超时时间
net.ipv4.tcp_max_tw_buckets = 256000 增大最多允许TIME_WAIT的数量
sysctl -p 使能新的配置
如果必须由服务器断开连接,这个问题如何优化?(参考链接:https://www.jianshu.com/p/2da62c5e10fa)
1,尽量调大系统TIME_WAIT连接数
net.ipv4.tcp_max_tw_buckets = 256000 最多允许time-wait数量,最大阈值
2,调整TIME_WAIT_2到TIME_WAIT的超时时间,默认是60s,优化到30s:
net.ipv4.tcp_fin_timeout = 30
3,下面再说一些linux里TIME_WAIT专有的优化参数reuse、recycle,默认都是关闭的,这两个参数必须在timestamps打开的前提下才能生效使用
注意:对于tw的reuse、recycle其实是违反TCP协议规定的,服务器资源允许、负载不大的条件下,尽量不要打开(副作用大)
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_reuse = 1
机器作为客户端时起作用,开启后time_wait在一秒内回收
net.ipv4.tcp_tw_recycle = 0 (建议不要开启,现在互联网NAT结构很多,可能直接无法三次握手)
开启后在3.5*RTO(RTO时间是根据RTT时间计算而来)内回收TIME_WAIT,并60s内同一源ip主机的socket connect请求中的timestamp必须是递增的,对于服务端,同一个源ip可能会是NAT后很多机器,这些机器timestamp递增性无可保证,服务器会拒绝非递增请求连接,直接导致不能三次握手。
经过权衡,最终决定,服务器暂时不开启副作用较大的tw参数,由客户端断开连接,修改客户机的配置。
修改后测试,16个slave实例,模拟480个用户压测查询接口,客户端与服务器均不再出现大量TIME_WAIT连接,RPS轻松上3k
服务器硬件配置较低,2核2G内存,不需要打的特别狠,满足需求即可