Node.js躬行记(14)——压力测试

本文涉及的产品
性能测试 PTS,5000VUM额度
简介:   公司有个匿名聊天的常规H5界面,运营向做一次 50W 的推送,为了能配合她的计划,需要对该界面做一次压力测试。

一、JMeter


  压测工具选择了JMeter,这是Apache的一个项目,它是用Java编写的,所以需要先安装Java的SDK,选择当前的操作系统。

211606-20211019182212938-1061044788.png


  随后到JMeter官网下载应用程序,选择 Binaries 中的压缩包

211606-20211019182307548-599826416.png


  在终端中进入解压后的 bin 目录,通过 sh jmeter 命令来启动 JMeter。


211606-20211019182341204-1916681022.png


  Don't use GUI mode for load testing:这段提示信息是不要在GUI界面进行压力测试,GUI界面仅仅用于调试。

  程序会自动打开 JMeter 的界面,如果在 选项 -》 选择语言 -》中文,那么有可能乱码。


211606-20211019182435473-1627894866.png


  只需选择 选项 -》 外观 -》System 或 Metal,就能避免乱码,网上有许多使用教程可以参考。

  当测试计划都编写完后,保存,然后在终端输入命令,就能开始压测了,其中目录相对于bin,couples.jmx 是测试计划,webreport是统计信息。

sh jmeter -n -t ../demo/couples.jmx -l ../demo/result/couples.txt -e -o ../demo/webrepo


211606-20211019182546495-647759489.png


二、实践


  在正式开始压测之前,也浏览了许多网络资料作为知识储备。

  首先需要理解Socket(套接字)的概念,它是对TCP/IP协议的封装,本身并不是协议,而是一个调用接口,Socket连接就是长连接。

  在创建Socket连接时,可以指定传输层协议,通常选择的是TCP协议,所以一旦通信双方建立连接后就开始互发数据,直至连接断开。

  而每个TCP都要占用一个唯一的本地端口号,但是每个端口并不会禁止TCP并发。

  然后去网上搜索了百万长连接可能遇到的瓶颈,包括TCP连接数、内存大小、文件句柄打开数等,例如:

  每个TCP连接都要占用一个文件描述符,而操作系统对可以打开的最大文件数的限制将会成为瓶颈。

  如果对本地端口号范围有限制(例如在1024~32768),当端口号占满时,TCP就会连接失败。

  网上给出了很多解决方案,大部分都是修改操作系统的各类参数。

1)开始测试

  上来就干,线程数直接填200以上。


211606-20211022113505178-835768421.png


  红框中的字段含义如下所示:

  • Label: 请求名称
  • #Smaples: 请求计数,其中108.4是TPS(每秒处理的事务数)
  • Average: 请求响应平均耗时
  • Min: 请求响应最小耗时
  • Max: 请求响应最大耗时
  • Error %: 请求错误率
  • Active:线程数(图中并未显示)

  查看报告页面,出现了多个错误,在网上查资源,做了些简单地挣扎,并没有得到好的解决办法。

Non HTTP response code: java.net.SocketException/Non HTTP response message: Connection reset
Non HTTP response code: javax.net.ssl.SSLHandshakeException/Non HTTP response message: Remote host terminated the handshake
Non HTTP response code: javax.net.ssl.SSLException/Non HTTP response message: java.net.SocketException: Connection reset
Non HTTP response code: java.net.SocketException/Non HTTP response message: Malformed reply from SOCKS server

  后面想想还是根据当前实际情况来吧,运营需要50W的推送,两小时内完成,平均每秒推送70条,将这个数据作为当前每秒的线程数,模拟后一切正常。

  注意,线程数和服务器的并发量不能完全画等号。


211606-20211022113633647-1549838077.png


  然后让4000个线程1分钟完成请求,配置Ramp-Up时间为60S,成功率是99.93%。

  图中的Ramp-Up时间指所有线程在多长时间(单位秒)内全部启动。例如500个线程10S,那么每秒启动 500/10=50 个线程,不写就是所有线程在开启场景后立即启动。

  再让5000的线程维持2分钟,配置Ramp-Up时间为120S,报无法创建新的本机线程的错误。

Uncaught Exception java.lang.OutOfMemoryError: unable to create new native thread in thread Thread[StandardJMeterEngine,5,main]

  为了解决此问题,期间走了很多误区,网上的很多资料都是说修改 jmeter.sh文件,像下面这样,但是改来改去仍然会报那错。

set HEAP=-server -Xms768m -Xmx768m -Xss128k 
set NEW=-XX:NewSize=1024m -XX:MaxNewSize=1024m

  或者是用命令来修改本机的一些参数,像下面这样,但仍然无济于事。

launchctl limit maxfiles 1000000 1000000
sysctl -w kern.maxfiles=100000
sysctl -w kern.maxfilesperproc=100000

  后面看到篇文章说在macOS中,对单个进程能够创建的线程数量是有限制的,下面的命令可以读取最大值,例如本机是4096,但该参数是只读的,无法修改。

sysctl kern.num_taskthreads

  于是马上就改变策略,一番查找下来,了解到JMeter还提供了一种远程模式。

2)远程模式

  既然一台机器的线程数有限,那可以通过多台机器来模拟更多的虚拟用户,JMeter有一种远程模式可以实现这个方案。

  首先需要在bin目录中的 jmeter.properties 文件修改remote_hosts参数,127.0.0.1改成本机地址,如下所示。

remote_hosts=192.168.10.10,192.168.10.46

  然后通过bin目录的create-rmi-keystore.sh生成rmi_keystore.jks,windows的可以直接运行create-rmi-keystore.bat,mac需要运行create-rmi-keystore.sh文件,会问你一堆问题。

sh create-rmi-keystore.sh

  并且需要将rmi_keystore.jks文件放置到从机的bin目录中。此时从机在开启sh jmeter-server时会报一个错误。

An error occurred: Cannot start. MacBook-Pro.local is a loopback address.

  修改jmeter-server,取消RMI_HOST_DEF的注释项,并将IP地址改成当前机器的。

RMI_HOST_DEF=-Djava.rmi.server.hostname=192.168.10.46

  一切准备就绪后,就可以使用压测命令了,与之前不同的是,需要加一个 -r 参数,其余照旧。

sh jmeter -n -t ../demo/couples.jmx -r -l ../demo/result/couples.txt -e -o ../demo/webreport

3)继续测试

  这次线程数量加到4000,加上从机,总共是1.2W个线程,Ramp-Up时间为60S,下面是结果图。


211606-20211022114028676-1473417860.png


  其中Throughput一列表示的是每秒处理的事务数(TPS),在此处也就是服务器的并发量。统计出21个错误,占比是0.17%。

Non HTTP response code: javax.net.ssl.SSLException/Non HTTP response message: Connection reset

  进到测试服务器,输入 ulimit -a 命令,open files 的数量有100多W,所以不会出现那种无法打开文件的错误。


211606-20211022114116431-69701321.png


  再详细的分析暂时不会,还得先去系统的学习一下,然后再回来补充。


三、学习性能测试


  为了学习性能测试,特地在网上找了个专栏《性能测试实战30讲》,顺便记录了些基础概念。

1)性能场景

  基准性能场景,单交易容量,将每一个业务压到最大TPS。

  容量性能场景,将所有业务根据比例加到一个场景中,在数据、软硬件、监控等的配合下,分析瓶颈并调优。

  稳定性性能场景,核心就是时长,在长时间的运行之下,观察系统的性能表现。

  异常性能场景,宕主机、宕应用、宕网卡、宕容器、宕缓存、宕队列等。

2)指标

  • RT:响应时间
  • TPS:每秒事务数
  • QPS:每秒SQL数
  • RPS:每秒请求数
  • Throughout:吞吐量

  所有相关的人都要知道TPS中的T是如何定义的。如果是接口层性能测试,T直接定义为接口级;如果是业务级性能测试,T直接定义为每个业务步骤和完整的业务流。

  对一个系统来说,如果仅在改变压力策略(其他的条件比如环境、数据、软硬件配置等都不变)的情况下,系统的最大 TPS 上限是固定的

    TPS = (1000ms(1秒)/ RT(单位ms))x 压力线程数

  对于压力工具来说,只要不报错,我们就关心 TPS 和响应时间就可以了,因为 TPS 反应出来的是和服务器对应的处理能力,至少压力线程数是多少,并不关键。

3)学习期

  性能工具学习期:自己有明确的疑问。通常所说的并发都是指服务端的并发,而不是指压力机上的并发线程数,因为服务端的并发才是服务器的处理能力。

  性能场景学习期:如何做一个合理的性能测试,调整业务比例,参数化数据的提取逻辑。

  性能分析学习期:面对问题应该是我想要看什么数据,而不是把数据都给我看看。

  通过你的测试和分析优化之后,性能提升了多少?

  通过你的测试和分析优化之后,节省了多少成本?

4)参数化

  参数化测试数据的疑问:

  • 参数化数据应该用多少数据量?
  • 参数化数据从哪里来?
  • 参数多与少的选择对系统压力有什么影响?
  • 参数化数据在数据库中的直方图是否均衡?

  在性能场景中,我们需要根据实际的业务场景来分析需要用到什么样的数据,以便计算数据量。

  参数化时需要确保数据来源以保证数据的有效性,千万不能随便造数据。这类数据应该满足两个条件:

  • 要满足生产环境中数据的分布;
  • 要满足性能场景中数据量的要求。


四、Websocket Bench


  在这次的压测中,想要测试2000人在线,并且同时聊天,服务器能否完美处理。

  如果要访问页面模拟用户的行为,会比较麻烦,因为在聊天前需要做两步操作,第一步是确认协议,第二步是选择匹配范围,第三步才开始匹配用户开始聊天。

  若要两个用户匹配成功,首先需要都在线,其次是经纬度计算后的范围满足之前的配置。

  为了避免那么多繁琐的前置场景,我决定直接对socket进行压测,于是想到了Websocket Bench

  它支持Socket.IO、Engine.IO、Primus等实时通信库的方法,经过简单的文档查阅后,开始编码,直接将官方demo复制修改。


module.exports = {
    /**
     * Before connection (optional, just for faye)
     * @param {client} client connection
     */
    beforeConnect : function(client) {
    },
    /**
     * On client connection (required)
     * @param {client} client connection
     * @param {done} callback function(err) {}
     */
    onConnect : function(client, done) {
      // Socket.io client
      client.emit('say', 100, {
        id: 111,
        avatar: 'http://www.pwstrick.com',
        userId: 123,
        msg: Date.now().toString(36) + Math.random().toString(36).substr(2),
        msgType: 'text'
      }, (msg) => {
        console.log(msg);
      });
      console.count();
      done();
    },
    /**
     * Send a message (required)
     * @param {client} client connection
     * @param {done} callback function(err) {}
     */
    sendMessage : function(client, done) {
      done();
    },
    /**
     * WAMP connection options
     */
    options : {
      // realm: 'chat'
    }
 };

  启动命令,-a 是指持久化连接总数 ,-c 是指每秒并发连接数 ,-g 是指要执行的JS文件,-k 保持活动连接,-o 是指日志的输出文件。

websocket-bench -a 2000 -c 2000 -g chat.js -k test-web-api.rela.me/chat -o opt.log

  开始运行后,并没有我设想的那样,实现2000人并发,TPS最多也就80多,到一个时间后,就持续变少。下图来自阿里云的日志,每次发消息我都会记录一条日志。


211606-20211101184017776-849456399.png


  我对上面的 -a 和 -c 的理解还有误差,不过也有可能是我本机限制了并发,之后就让QA在服务器上调试了。


相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
2月前
|
Web App开发 JavaScript 前端开发
添加浮动按钮点击滚动到网页底部的纯JavaScript演示代码 IE9、11,Maxthon 1.6.7,Firefox30、31,360极速浏览器7.5.3.308下测试正常
添加浮动按钮点击滚动到网页底部的纯JavaScript演示代码 IE9、11,Maxthon 1.6.7,Firefox30、31,360极速浏览器7.5.3.308下测试正常
|
25天前
|
人工智能 监控 JavaScript
模拟依赖关系和 AI 是Vue.js测试的下一个前沿领域
模拟依赖关系和 AI 是Vue.js测试的下一个前沿领域
18 1
|
28天前
|
JavaScript 前端开发
JavaScript - 测试 Prototype
JavaScript - 测试 Prototype
9 0
|
28天前
|
JavaScript 前端开发
JavaScript - 测试 jQuery
JavaScript - 测试 jQuery
9 0
|
3月前
|
Web App开发 应用服务中间件 定位技术
three.js:三维模型加载量测试
three.js:三维模型加载量测试
135 4
|
3月前
|
JavaScript 前端开发 测试技术
Vue.js开发者必看!Vue Test Utils携手端到端测试,打造无懈可击的应用体验,引领前端测试新风尚!
【8月更文挑战第30天】随着Vue.js的普及,构建可靠的Vue应用至关重要。测试不仅能确保应用质量,还能提升开发效率。Vue Test Utils作为官方测试库,方便进行单元测试,而结合端到端(E2E)测试,则能构建全面的测试体系,保障应用稳定性。本文将带你深入了解如何使用Vue Test Utils进行单元测试,通过具体示例展示如何测试组件行为;并通过Cypress进行E2E测试,确保整个应用流程的正确性。无论是单元测试还是E2E测试,都能显著提高Vue应用的质量,让你更加自信地交付高质量的应用。
63 0
|
3月前
|
JavaScript 前端开发 应用服务中间件
【qkl】JavaScript连接web3钱包,实现测试网络中的 Sepolia ETH余额查询、转账功能
【区块链】JavaScript连接web3钱包,实现测试网络中的 Sepolia ETH余额查询、转账功能
|
5月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的高中信息技术课程在线测试系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的高中信息技术课程在线测试系统附带文章和源代码部署视频讲解等
49 6
|
5月前
|
前端开发 JavaScript 算法
JavaScript事件监听测试代码
JavaScript事件监听测试代码
32 0
|
6月前
|
前端开发 测试技术
Node 单元测试
Node 单元测试
37 0