热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:性能分析之长连接全链路压测

篇首语:本文由编程笔记#小编为大家整理,主要介绍了性能分析之长连接全链路压测相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了性能分析之长连接全链路压测相关的知识,希望对你有一定的参考价值。




一、背景:



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



二、全链路架构图:


  性能分析之长连接全链路压测


三、遇到的问题总结:



问题一: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。报错如下:


性能分析之长连接全链路压测

  



此时的jmeter启动参数如下:


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


发现jvm设置的内存很大,有15g,百度谷歌一番,得知:


性能分析之长连接全链路压测

  



于是,将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。如下:


  性能分析之长连接全链路压测

  



reload nginx生效后问题解决。




继续压测,使用50台肉鸡,每台启动1w线程建立连接。在连接数达到50w保持心跳连接时,开始发送数据出现大批量掉线(发送的数据会造成使得在同一房间的连接都会收到消息,即:广播)。




首先,使用以下命令查看一层Nginx和Ingres的连接状况:


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


发现Nginx和Ingress都出现了大量TIME_WAIT,说明连接是代理层主动断开的(主动断开连接的一方会进入TIME_WAIT状态),此时查看Nginx和Ingress日志并没有发现任何的报错。询问消费服务网关开发同学是否有报错日志,开发同学反馈是客户端主动断开了连接,但是没有更加具体的报错。查看肉鸡Jmeter的日志,有如下报错:


  性能分析之长连接全链路压测

  



这是jmeter的第三方websocket的jar报出来的错,也是显示连接不可用。日志都没有具体的问题,那么到底是什么原因导致连接被断掉呢?开始在整条链路上抓包分析,每一个节点都抓取上下游的包,抓包命令如下:


# tcpdump -i any host xxx.xxx.xxx.xxx -v -w client.pcap
//tcpdump抓取的报文通常会很大,可以使用wireshark自动的editcapmergecap工具根据时间来分隔和合并报文


分析报文,发现肉鸡发送大量的窗口满的报文,由Nginx和Ingress代理到后端服务网关。如下是Ingress到服务网关的报文:


性能分析之长连接全链路压测



Ingress发送TCP ZeroWindow的报文,最后会RST连接。那么肉鸡为什么会发送窗口满的报文呢?查看全链路的带宽情况:


性能分析之长连接全链路压测

  



查看监控发现网络和带宽都是没有问题的。于是将重点指向肉鸡,初步怀疑是肉鸡的websocket jar包在处理网络数据的机制上有问题。经过一番搜索,发现如下:


性能分析之长连接全链路压测

  



在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连接四元组可知:NginxIP * Nginx开启的随机端口数 * IngressIP * Ingress服务端口数 = 1 * 65535 * 1 * 1 = 65535 正常情况下理论上是支持65535TCP连接的,
  但是随机端口数0~1024一般作为服务端口被占用,所以需要去除掉一些常用的端口,并预留一部分端口。所以开启10240~65000大概5.5w个端口数。
5.在充分利用机器资源的情况下,支持50w+TCP连接数的瓶颈:
  第一,压测到Nginx服务端,瓶颈在于增加压测机的数量;
  第二,NginxIngress,增加Ingress服务端口数,开启多个服务加到Nginxupstream中来扩充四元组中的Ingress服务端口数;
  第三,Ingress到服务端,增加服务端的pod数加到Ingressupstream来扩充四元组的服务端端口数。所以需要关注这三个点的TCP连接数的支持情况。
所以解决nginx端口耗尽的问题可以在nginx上增加upstream数量,upstream可以是不同的ip+port,也可以是同一个ip下的不同port,还有就是可以在nginx主机上增加IP地址,然后使用nginxproxy_bind指定源地址。

于是查看一层Nginx的/proc/sys/net/ipv4/ip_local_port_range的值,设置为21000-61000,端口数为4w,后端后5个Ingress,也就是每个Nginx能支持20w的连接,一共4个Nginx,也就是:4*20w=80w。排除其他和压测无关的连接后,和72w相差不大,于是调整改参数为:1024-65530,理论上估算能支持:4*5*6w=120w并发连接。但是,Nginx的连接数还取决于worker_rlimit_nofile和worker_connections两个参数,如下: 性能分析之长连接全链路压测

  其中worker_rlimit_nofile是文件句柄数,设置该值会覆盖系统的/etc/security/limits.conf的最大文件数。可以通过查看nginx进程的限制来验证:

性能分析之长连接全链路压测

  

并且由于worker_connections这个参数会在Nginx启动时预先分配内存,所以这个值并不是设置的越大越好,应该根据实际场景来设置大小。可以通过调整改值后重启Nginx时通过# free -m 查看nginx的初始占用内存大小来验证。在32个woker下,改值设置10w时,初始化内存大概为3G;设置100w时,初始化内存大概14G。

优化完参数后重启Nginx,并发数能稳定支持100w。其中一台Nginx的连接:

性能分析之长连接全链路压测

  



继续压测,当连接稳定在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
 

性能分析之长连接全链路压测

  



此时,再次全链路抓包,查看服务器负载和带宽情况(说明系统监控的重要性,我们使用的是Grafana+Prometheus+Alertmanager+node_exporter监控栈)。


在jmeter客户端抓到的包可以看到有较多的零窗口,如下所示:

性能分析之长连接全链路压测

  



此时查看Nginx和肉鸡两端的网络连接状态,使用 # ss -tn 命令可以看到大量 ESTABLISHED 状态连接的 Send-Q 堆积很大,客户端的 Recv-Q 堆积很大。Nginx 端的 ss 部分输出如下所示:


性能分析之长连接全链路压测

  



并使用# dstat 命令查看系统性能状态:


  

  



可以看到,最后两列中系统CPU中断和上下文切换开销都很大。系统负载高。




此时,定位到是jmeter肉鸡处理能力有限,有较多的消息堆积在中转的Nginx中,导致Nginx内存不断飙升直到OOM。于是,增加肉鸡到200台,每台肉鸡线程数从1w降到5000。此时发现,压测能正常进行,但是Nginx内存仍然在上升,只是对比之前上升的稍微缓慢一些。再次抓包分析,肉鸡还是偶尔出现零窗口。于是想到,Nginx是否可以不缓存消息?通过分析Nginx的配置参数,发现proxy_buffers这个值设置很大,如下:



  



查看官网相关配置项,关闭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 左右。



推荐阅读
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • JavaScript简介及语言特点
    本文介绍了JavaScript的起源和发展历程,以及其在前端验证和服务器端开发中的应用。同时,还介绍了ECMAScript标准、DOM对象和BOM对象的作用及特点。最后,对JavaScript作为解释型语言和编译型语言的区别进行了说明。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 一、Struts2是一个基于MVC设计模式的Web应用框架在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2优点1、实现 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • express工程中的json调用方法
    本文介绍了在express工程中如何调用json数据,包括建立app.js文件、创建数据接口以及获取全部数据和typeid为1的数据的方法。 ... [详细]
author-avatar
Adonis-唯一
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有