• 【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)
    
    • 1
    • 2
    • 3

    问题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
    
    • 1

    注:不过这个值只是在当前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))
    
    • 1

    服务端有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
    
    • 1
    • 2
    • 3

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

    sysctls:
      net.core.somaxconn: 2048
      net.ipv4.tcp_max_syn_backlog:4000
    
    • 1
    • 2
    • 3

    或者

    sysctls:
      - net.core.somaxconn=2048
      - net.ipv4.tcp_max_syn_backlog=4000
    
    • 1
    • 2
    • 3

    注:

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

    检测tcp状态

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

    脚本 print_tcp_conn_stat.sh, 内容如下,

    #!/bin/bash
    
    echo "TIME_WAIT  :"`netstat -tuna | grep 5000 | grep "TIME_WAIT" | wc -l`
    echo "ESTABLISH  :"`netstat -tuna | grep 5000 | grep "ESTABLISH" | wc -l`
    echo "CLOSE_WAIT :"`netstat -tuna | grep 5000 | grep "CLOSE_WAIT" | wc -l`
    echo "SYN_SENT  :"`netstat -tuna | grep 5000 | grep "SYN_SENT" | wc -l`
    echo "SYN_RECV  :"`netstat -tuna | grep 5000 | grep "SYN_RECV" | wc -l`
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    效果如下,
    效果如下

    这几个状态说明一下:

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

    使用keepalive优化服务能力

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

    gunicorn
    --keep-alive 30
    
    • 1
    • 2

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

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

  • 相关阅读:
    SpringBoot通过@Cacheable注解实现缓存功能
    计算机组成原理学习笔记:BCD码
    StatefulSet
    1-图像读取
    MODBUS转PROFINET网关与安科瑞ard3t电机保护器的连接方法
    图文超详细解决IDEA使用Git忽略提交一个某个文件
    MySQL 创建用户,修改用户,授权,删除用户等操作命令
    大数据安全建设面临哪些挑战
    数据分析和可视化平台:Splunk Enterprise for mac v9.1.1激活版 兼容m1
    ISP Pipeline典型场景要点SAT、Bokeh、MFNR、HDR
  • 原文地址:https://blog.csdn.net/mimiduck/article/details/126390124