【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/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`

然后使用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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python项目代码使用过程,可能会遇到各种各样的问题。为了更好地记录和解决这些问题,我们可以建立一个Python问题汇总索引目录,以便随时查阅和解决。 1. 数据类型问题:比如在处理数据时,遇到了不同数据类型的转换问题,如将字符串转为数字或者将数字转为字符串等。 2. 模块导入问题:在使用第三方模块时,可能会遇到无法导入模块的问题,需要查找原因并解决。 3. 语法错误:在编写代码时,可能会出现各种语法错误,如拼写错误、缩进错误等,需要及时修改以保证代码的正确运行。 4. 性能优化问题:在项目运行过程,可能会遇到性能瓶颈,需要进行代码优化以提高项目的运行效率。 5. 异常处理问题:在项目运行过程,可能会遇到各种异常情况,需要进行异常处理以保证程序的稳定性。 6. 接口调用问题:在项目调用外部接口时,可能会遇到接口参数传递或者返回结果处理的问题,需要及时解决。 7. 单元测试问题:在编写项目代码时,可能会遇到单元测试用例编写或者执行结果验证的问题,需要及时进行检查和修复。 8. 版本兼容性问题:在使用第三方库或者Python版本升级时,可能会出现版本兼容性问题,需要及时调整或者更新相关的代码。 总之,建立Python问题汇总索引目录可以帮助我们更好地管理和解决在项目代码使用过程遇到的各种问题,提高项目的开发和运行效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值