百万长连接压测问题排查分析

简介: websocket长连接性能压测,及调优

一、背景:
  基于WebSocket长连接的消息服务进行全链路压测,目标是实现最少100W长连接下压测服务的各个接口TPS,QPS及其稳定性和资源消耗情况。

二、全链路架构图:
image
三、遇到的问题总结:
  问题一:Jmeter肉鸡连接达到1w左右时,出现OOM。
  问题二:心跳超时导致连接断开。
  问题三:达到50w并发时,出现连接大批量掉线问题。
  问题四:达到72w并发时,出现连接数上不去的问题。
  问题五:达到100w并发稳定建立并保持时,出现发送数据掉线问题,此时Nginx OOM。

  其中肉鸡的内核参数设置如下:

cat >> /etc/sysctl.conf << EOF

net.ipv4.tcp_max_tw_buckets = 200000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 11000 61000
fs.file-max = 1000000
net.ipv4.ip_conntrack_max = 2000000
net.ipv4.netfilter.ip_conntrack_max = 2000000
net.nf_conntrack_max = 2000000
net.netfilter.nf_conntrack_max = 2000000
net.ipv4.tcp_max_orphans = 500000
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
EOF

sysctl -p

//设置文件句柄数,其实不需要设置100w这么大,根据肉鸡的连接数设置合理即可

sed -i 's/65535/1000000/g' /etc/security/limits.conf

四、压测过程问题排查分析:
  在搭建,调试好全链路压测环境后启动一台Jmeter肉鸡进行测试,发现当肉鸡连接数达到1w时出现OOM。报错如下:
image
发现jvm设置的内存很大,有15g,百度谷歌一番,得知:
image
 于是,将jmeter的jvm设置成4g,如下:

cd /root/apache-jmeter-5.1.1/bin/ && HEAP="-Xms4g -Xmx4g" ./jmeter-server -Djava.rmi.server.hostname=xxx.xxx.xxx.xxx -Jserver.rmi.ssl.disable=true &> /tmp/jmeter.log &

调整之后单台jmeter肉鸡连接数能达到2w并且内存还很充足。后续所有肉鸡都是用此参数启动进程。到此,开始进行压测。
开始压测50w的并发建连,建立连接后3分钟左右出现断线,进行分析是因为在没有数据发送的情况下,Nginx配置了180s的超时时间。超过180s后主动断掉连接。通过和开发沟通,将proxy_connect_timeout,proxy_send_timeout和proxy_read_timeout都设置为900s。如下:
image
eload nginx生效后问题解决。
  继续压测,使用50台肉鸡,每台启动1w线程建连。在连接数达到50w保持心跳连接时,开始发送数据出现大批量掉线(发送的数据会造成使得在同一房间的连接都会收到消息,即:广播)。
  首先,使用一下命令查看一层Nginx和Ingres的连接状况:
  
1

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

  发现Nginx和Ingress都出现了大量TIME_WAIT,说明连接是代理层主动断开的(主动断开连接的一方会进入TIME_WAIT状态),此时查看Nginx和Ingress日志并没有发现任何的报错。询问消费服务网关开发同学是否有报错日志,开发同学反馈是客户端主动断开了连接,但是没有更加具体的报错。查看肉鸡Jmeter的日志,有如下报错:
image
这是jmeter的第三方websocket的jar报出来的错,也是显示连接不可用。日志都没有具体的问题,那么到底是什么原因导致连接被断掉呢?开始在整条链路上抓包分析,每一个节点都抓取上下游的包,抓包命令如下:
  

tcpdump -i any host xxx.xxx.xxx.xxx -v -w client.pcap

//tcpdump抓取的报文通常会很大,可以使用wireshark自动的editcap和mergecap工具根据时间来分隔和合并报文
分析报文,发现肉鸡发送大量的窗口满的报文,由Nginx和Ingress代理到后端服务网关。如下是Ingress到服务网关的报文:
image
 Ingress发送TCP ZeroWindow的报文,最后会RST连接。那么肉鸡为什么会发送窗口满的报文呢?查看全链路的带宽情况:
image
查看监控发现网络和带宽都是没有问题的。于是将重点指向肉鸡,初步怀疑是肉鸡的websocket jar包在处理网络数据的机制上有问题。经过一番搜索,发现如下:
image
image
在jmeter-websocket-samplers-1.2.2.jar的官网搜索到作者的最新版本说解决了该问题,于是替换jmeter-websocket-samplers-1.2.2.jar包为最新的JMeterWebSocketSamplers-1.2.6.jar版本。实测无效,问题依旧。再次重点分析肉鸡,查看监控,发现肉鸡在出现掉线的时候负载很高,load average高达200+。判断是肉鸡负载过高,处理不过来导致tcp滑动窗口满,最终断开连接的问题。
  于是,增加肉鸡到100台,每台肉鸡还是开启1w线程,看只建立100w连接不发送数据的情况下是否稳定。发现在连接数达到72w左右时连接数上不去了。于是,分析全链路能支持的并发数。要计算全链路支持的并发数需要了解以下知识点:
TCP连接知识点:
1.一个TCP连接的套接字对(socket pari)是一个定义该连接的两个端点的四元组,即本地IP地址、本地TCP端口号、外地IP地址、外地TCP端口号。套接字对唯一标识一个网络上的每个TCP连接。
2.linux socket使用16bit无符号整型表示端口号,最大到65535。也就是说一台客户端的机器上的一个IP对应有65535个端口号可以用于对服务端建立TCP连接,而服务器的服务端口号一般是启用端口复用的,
  也就是一个服务端口可以支持多个TCP连接,epoll模式理论上支持的连接数没有上限。
3.使用nginx作为反向代理时,nginx即是服务端,又是客户端。作为服务端,服务的端口号对客户端是复用的,然后作为客户端使用本机的其他1024~65535端口号和后端的服务器建立连接实现代理。
  这样,一个TCP连接在反向代理的nginx机器上表现为有两个TCP连接,即占用两个socket文件句柄数。
4.计算nginx或者ingress支持的TCP连接数计算方法,以nginx为例,根据tcp连接四元组可知:Nginx的IP数 Nginx开启的随机端口数 Ingress的IP数 Ingress服务端口数 = 1 65535 1 1 = 65535 正常情况下理论上是支持65535个TCP连接的,
  但是随机端口数0~1024一般作为服务端口被占用,所以需要去除掉一些常用的端口,并预留一部分端口。所以开启10240~65000大概5.5w个端口数。
5.在充分利用机器资源的情况下,支持50w+的TCP连接数的瓶颈:
  第一,压测到Nginx服务端,瓶颈在于增加压测机的数量;
  第二,Nginx到Ingress,增加Ingress服务端口数,开启多个服务加到Nginx的upstream中来扩充四元组中的Ingress服务端口数;
  第三,Ingress到服务端,增加服务端的pod数加到Ingress的upstream来扩充四元组的服务端端口数。所以需要关注这三个点的TCP连接数的支持情况。
    所以解决nginx端口耗尽的问题可以在nginx上增加upstream数量,upstream可以是不同的ip+port,也可以是同一个ip下的不同port,还有就是可以在nginx主机上增加IP地址,然后使用nginx的proxy_bind指定源地址。

 于是查看一层Nginx的/proc/sys/net/ipv4/ip_local_port_range的值,设置为21000-61000,端口数为4w,后端后5个Ingress,也就是每个Nginx能支持20w的连接,一共4个Nginx,也就是:420w=80w。排除其他和压测无关的连接后,和72w相差不大,于是调整改参数为:1024-65530,理论上估算能支持:45*6w=120w并发连接。但是,Nginx的连接数还取决于worker_rlimit_nofile和worker_connections两个参数,如下:
image
其中worker_rlimit_nofile是文件句柄数,设置该值会覆盖系统的/etc/security/limits.conf的最大文件数。可以通过查看nginx进程的限制来验证:
image
并且由于worker_connections这个参数会在Nginx启动时预先分配内存,所以这个值并不是设置的越大越好,应该根据实际场景来设置大小。可以通过调整改值后重启Nginx时通过# free -m 查看nginx的初始占用内存大小来验证。在32个woker下,改值设置10w时,初始化内存大概为3G;设置100w时,初始化内存大概14G。
  优化完参数后重启Nginx,并发数能稳定支持100w。
  继续压测,当连接稳定在100w时开始发送数据,出现Nginx内存飙升,最后频繁OOM,伴随着TCP重传率高达40%-50%。报错和监控如下(原本Nginx是64G内存,后因为该问题升级到128G内存后问题依旧):
  
[Fri Mar 13 18:46:44 2020] Out of memory: Kill process 28258 (nginx) score 30 or sacrifice child
[Fri Mar 13 18:46:44 2020] Killed process 28258 (nginx) total-vm:1092198764kB, anon-rss:3943668kB, file-rss:736kB, shmem-rss:4kB
image
 此时,再次全链路抓包,查看服务器负载和带宽情况(说明系统监控的重要性,我们使用的是Grafana+Prometheus+Alertmanager+node_exporter监控栈)。
在jmeter客户端抓到的包可以看到有较多的零窗口,如下所示:
image
此时查看Nginx和肉鸡两端的网络连接状态,使用 # ss -tn 命令可以看到大量 ESTABLISHED 状态连接的 Send-Q 堆积很大,客户端的 Recv-Q 堆积很大。Nginx 端的 ss 部分输出如下所示:
image
并使用# dstat 命令查看系统性能状态:
image
可以看到,最后两列中系统CPU中断和上下文切换开销都很大。系统负载高。
  此时,定位到是jmeter肉鸡处理能力有限,有较多的消息堆积在中转的Nginx中,导致Nginx内存不断飙升直到OOM。于是,增加肉鸡到200台,每台肉鸡线程数从1w降到5000。此时发现,压测能正常进行,但是Nginx内存仍然在上升,只是对比之前上升的稍微缓慢一些。再次抓包分析,肉鸡还是偶尔出现零窗口。于是想到,Nginx是否可以不缓存消息?通过分析Nginx的配置参数,发现proxy_buffers这个值设置很大,如下:
image
查看官网相关配置项,关闭proxy_buffering,调小proxy_buffer_size 和 proxy_buffers,注释proxy_busy_buffers_size。如下:
proxy_buffering off;
proxy_buffer_size 4k;
proxy_buffers 4 8k;

proxy_busy_buffers_size 256M;

  经过实测,在压测环境修改了这个值以后,以及调小了 proxy_buffer_size 的值以后,内存稳定在了 20G 左右,没有再飙升过。后面可以开启 proxy_buffering,调整 proxy_buffers 的大小可以在内存消耗和性能方面取得更好的平衡。

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
目录
相关文章
|
7月前
|
负载均衡 测试技术 应用服务中间件
性能测试常见瓶颈分析及调优方法总结
性能测试常见瓶颈分析及调优方法总结
261 0
|
2月前
|
监控 Cloud Native 测试技术
PTS 3.0:开启智能化的压测瓶颈分析
PTS 3.0:开启智能化的压测瓶颈分析
205552 116
|
4月前
|
NoSQL 测试技术 Redis
Redis【性能 01】Redis 5.x 6.x 7.x 共5个不同版本延迟及性能测试对比分析(单机版默认配置)
Redis【性能 01】Redis 5.x 6.x 7.x 共5个不同版本延迟及性能测试对比分析(单机版默认配置)
199 0
|
5月前
|
存储 缓存 NoSQL
Redis性能测试实操记录与分析
通过对Redis性能测试的实操记录和分析,我们对Redis的性能表现有了更深入的了解。这种性能测试可以帮助我们评估Redis在不同负载下的表现,并根据测试结果采取相应的优化策略,以确保Redis在实际应用中能够满足性能需求,并提供高速的数据存储和缓存解决方案。
113 0
|
7月前
|
测试技术 数据库
接口并发性能测试开发之:从测试方案设计、测试策略、指标分析到代码编写,这一篇全搞定。
接口并发性能测试开发之:从测试方案设计、测试策略、指标分析到代码编写,这一篇全搞定。
149 0
|
SQL 监控 测试技术
如何分析并设计性能测试场景
以上就是关于性能需求分析以及场景设计的内容,文中举的例子仅供参考,在实际的工作中,需要学会灵活变通。
如何分析并设计性能测试场景
|
弹性计算 网络协议 Ubuntu
阿里云ECS压测工具分析-网络性能
近段关注阿里云ECS指标分析,现梳理下相关的压测工具并记录。
2544 0
阿里云ECS压测工具分析-网络性能
|
缓存 负载均衡 监控
如何分析性能测试需求?
为了避免某个可用区由于网络硬盘等原因损坏导致服务不可用,跨可用区的服务部署是一种常见的容灾手段。
如何分析性能测试需求?
|
缓存 负载均衡 监控
性能测试知识科普(四):分析需求
为了避免某个可用区由于网络硬盘等原因损坏导致服务不可用,跨可用区的服务部署是一种常见的容灾手段。
性能测试知识科普(四):分析需求
|
SQL 运维 监控
性能测试常见瓶颈分析及调优方法
事务成功率在某些时候也可以视为请求成功率,在断言判断时以code/status等内容来作为请求是否成功的衡量依据;
性能测试常见瓶颈分析及调优方法