0


【python】HTTP压力测试过程中遇到的问题与解决方案

记录一下测试过程中遇到的问题

背景

被测试HTTP服务在容器中运行,使用的是gunicorn,在另外一台server通过python requests做压力测试

问题1. urllib3 connection pool full

urllib3.connectionpool Connection pool is full

requests使用了urlib3,urllib3中有PoolManager,它会复用连接,所以如果压测过程中,大量发起requests,会导致PoolManager中的connection pool满掉,进而出现这个问题。

【解决方案】

根据你的量适当调整pool_connections的值

    session = requests.session()
    adapter = requests.adapters.HTTPAdapter(pool_connections=1000, pool_maxsize=4000)
    session.mount("http://", adapter)

问题2.Too many open files

Failed to establish a new connection: [Errno 24] Too many open files

每一个连接会打开一个socket,一个socket会使用一个句柄,可以通过

ulimit -n

查看当前系统的open file

默认是1024

在做压力测试的机器上,需要修改该值

【解决方案】

ulimit -n 65535

注:不过这个值只是在当前session中做了修改,如果下次登陆时还需要再做修改;


问题3 requests.exceptions.ConnectTimeout

客户端大量连接出现Connect timeout错误

原因有两个:

  1. 客户端的connect time设置时间过短
  2. 服务端的syn backlog设置过小

【解决方案】

客户端的connect time设置,这里主要是requests库里配置超时的地方注意一下

requests.post(url, data=data, headers=headers, timeout=(CONNECTION_TIMEOUT, REQUEST_TIMEOUT))

服务端有2个参数可以配置

  • netdev_max_backlog 位置/proc/sys/net/core/netdev_max_backlog,主要控制当kernel无法及时处理时接收到的packets的队列大小
  • tcp_max_syn_backlog 位置/proc/sys/net/ipv4/tcp_max_syn_backlog,tcp协议栈在收到客户端发送的SYN消息后会回复SYNACK同时将此消息放入队列,等待客户端发送ACK确认

问题4 requests.exceptions.ConnectionError:Connection reset by peer

'Connection reset by peer'

在客户端,大量的tcp连接被reset了,这里我们需要检测被测试系统的tcp backlog值是否足够,如果不够,服务连接达到瓶颈时,可能会出现该问题。

【解决方案】

tcp的backlog主要有2个地方配置

  • listen backlog 应用服务的tcp listen时,有一个参数是backlog,作为服务端,这个值不可以过小,请根据服务器物理性能适当设置
  • somaxconn 位置/proc/sys/net/core/somaxconn,控制ESTABLISHED的接连数量。 一些系统中默认是128,作为服务器显然是不足的,需要往上调整。 注意,如果不调整somaxconn,仅调整listen函数中的backlog,最终的结果是无效的。 比如listen函数中的backlog中设置1024,但是默认的somaxconn=128,实际上还是128.

问题5 Possible SYN flooding

通过

dmesg -T

查看系统消息时,如果有如下消息

TCP: request_sock_TCP: Possible SYN flooding on port xxx. Sending cookies

是大量SYN消息收到了,存入了SYN-ACK队列,但是没有被处理。
这可能是因为tcp的backlog设置过小,或者服务器处理性能不足导致的;对于前者请参考问题4解决方案,后者请优化服务性能。


问题6 requests.exceptions.BrokenPipeError

压测客户端出现如下报错

[Errno 32] Broken pipe

查阅资源是当往一个已经close的socket写时,会收到SIGPIPE。

This might be happening when a client program doesn’t wait till all the data from the server is received and simply closes a socket (using close function).

产生这个问题的原因是,我在线程中post数据,但是再压测程序最后的主进程中,在线程socket未结束的时候,直接close了socket。


其他注意事项

在docker以镜像方式部署的时候,请检查一下所用镜像中的系统配置,
比如:
open files是否是默认的1024?这个值肯定是过小的;
上述几个backlog参数是否是默认值?

一般镜像中的系统配置是只读的,需要在docker run的时候通过携带参数的方式来修改

比如修改somaxconntcp_max_syn_backlog的方式如下

docker run 
--sysctl net.core.somaxconn=2048 
--sysctl net.ipv4.tcp_max_syn_backlog=4000

如果是使用docker-compose方式来启动的可以在yaml文件中添加如下

sysctls:
  net.core.somaxconn: 2048
  net.ipv4.tcp_max_syn_backlog:4000

或者

sysctls:
  - net.core.somaxconn=2048
  - net.ipv4.tcp_max_syn_backlog=4000

注:

  1. netdev_max_backlog参数在容器中是没有的,只能修改宿主机配置
  2. 容器里的参数与宿主机的参数不冲突,如果两者不一致以镜像中的参数为实际运行结果

检测tcp状态

可以通过脚本实时检测tcp的状态变化
下面是我写的脚本,用于检测服务端口,这里我的服务端口是5000

脚本 print_tcp_conn_stat.sh, 内容如下,

#!/bin/bashecho"TIME_WAIT  :"`netstat -tuna |grep5000|grep"TIME_WAIT"|wc -l`echo"ESTABLISH  :"`netstat -tuna |grep5000|grep"ESTABLISH"|wc -l`echo"CLOSE_WAIT :"`netstat -tuna |grep5000|grep"CLOSE_WAIT"|wc -l`echo"SYN_SENT  :"`netstat -tuna |grep5000|grep"SYN_SENT"|wc -l`echo"SYN_RECV  :"`netstat -tuna |grep5000|grep"SYN_RECV"|wc -l`

然后使用watch命令,来做实时监控,

# 每1s监控一次watch -d -n 1 ./print_tcp_conn_stat.sh

效果如下,
效果如下

这几个状态说明一下:

  • TIME_WAIT 客户端结束时,socket的状态
  • ESTABLISH tcp连接建立后的状态
  • CLOSE_WAIT 服务端结束时,socket的状态
  • SYN_SENT 客户端发送SYN
  • SYN_RECV 服务端接收SYN

使用keepalive优化服务能力

如果你也使用gunicorn,可以在启动gunicorn的命令中添加如下参数,

gunicorn
--keep-alive 30

使用了keepalive之前,每一次tcp访问,都会经理SYN_SENT -> ESTABLISH -> CLOSE_WAIT 过程
(因为我客户端也复用了连接,所以没有TIME_WAIT)

使用keepalive后,服务器建立连接后,状态会一直保持在ESTABLISH,直到keepalive的timeout到了之后,服务端才结束连接,出现CLOSE_WAIT


本文转载自: https://blog.csdn.net/mimiduck/article/details/126390124
版权归原作者 安安爸Chris 所有, 如有侵权,请联系我们删除。

“【python】HTTP压力测试过程中遇到的问题与解决方案”的评论:

还没有评论