用jmeter对websocket应用进行性能测试

准备测试环境

参考文章:

https://blog.csdn.net/ifrozen/article/details/53127542

插件的github地址:

https://github.com/maciejzaleski/JMeter-WebSocketSampler

依赖包下载地址(下载后解压,lib目录中有所需的全部6个依赖jar文件):

https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.1.1.v20140108/jetty-distribution-9.1.1.v20140108.zip

测试方法

参考文章:

https://blog.csdn.net/ifrozen/article/details/53127542

这个文章只是简单展示了如何配置测试,只能测试websocket的连接性能,连上就会自动断开,不能测试同时能支持的长连接数量。

关于聚合报告的字段说明:

  1. Samples:样本总数量,等于线程总数 * 循环次数
  2. Average:请求处理的平均时间(毫秒ms),是压力测试的主要指标之一
  3. Median:请求处理的中值时间(毫秒ms),样本数量中有一半的处理时间在这个值之上,有一半的处理时间在这个值之下
  4. 90%Line,95%Line,99%Line:样本中百分之多少的处理时间都在这个值之下,是压力测试的主要指标之一
  5. Min:耗时最少的请求时间
  6. Max:耗时最多的请求时间
  7. Error%:错误率
  8. Throughput:吞吐量,服务器每秒处理的请求数
  9. KB/sec:服务器每秒钟请求的字节数

如果要测试保持连接的情况,需要勾选Streaming connection,且在线程组里面,要将循环次数设置为永远(否则连接创建后会自动关闭的),可以参考这个文章:

https://blog.csdn.net/weixin_41044523/article/details/79290053

如何测试消息的到达时间呢?

1.浏览器先打开一个连接,假设叫做A

2.jmeter创建5000个连接

3.浏览器再打开一个连接,假设叫做B

然后通过A连接,给所有连接发送消息,从消息发出,到B收到这个消息,就是整体的推送时间了。

测试结果

阶段一:本地单人测试

程序部署在我本地电脑上,jmeter也在我本地电脑。

测试了下,发现客户端收到推送的消息,很慢,要过好多秒才行。

创建连接时,耗费的CPU很高,基本拉满了。

发消息时,CPU会飙升,不过很短暂,马上就恢复正常了。

维持连接,耗费的cpu不多,主要是耗费内存:

关闭1000个连接,内存从7.55G降低到6.65G,也就是1G内存能支持大约1100个连接;

关闭2000个连接,内存从7.33G降低到5.64G,也就是1G内存能支持大约1538个连接。

另外,发现关闭连接的速度很慢,关掉2000个连接要好多秒;这可能是jmeter的问题,它是逐个关闭的。

阶段二:服务器多人测试

我们将程序部署到了测试服务器上(4核4G,配置比较差),然后在本地通过jmeter进行测试。

总共找了6名同事,每个人的jmeter连接数设置的都是1500,一起开始测试,然后发现服务端的连接数到了3000多就上不去了。

而此时服务器的CPU和内存都不高,这个现象不科学,我们怀疑是服务器的相关配置影响了这个测试。

阶段三:修改服务器连接数

通过ulimit -a查看服务器的最大文件打开数,发现设置为了102400,超过了65535,应该是取的最大可用值65535,那么这个配置没有问题。

然后查看Nginx的配置,发现worker_rlimit_nofile默认没有设置,worker_connections设置的是1024,因此怀疑和这2个配置有关系。

修改nginx配置,如下:

1
2
3
4
5
worker_rlimit_nofile  65535;
events {
use epoll;
worker_connections 65535;
}

然后再发起测试。这次只用了一个客户端测试,设置连接数5000,发现服务端的连接数超过了上次的3000+;继续增加客户端数量(增加了2个客户端,每个1500连接数),最终服务端连接数达到了7698(5000连接数那个客户端,有部分失败的连接,因此三个客户端加起来没有达到8000),说明影响我们测试的关键因素,确实是Nginx的这2个配置。

另外,我调整了这2个参数,准备验证下每个参数对测试的影响:

1
2
3
4
5
#worker_rlimit_nofile  65535;
events {
use epoll;
worker_connections 65535;
}

这种情况,连接创建得比较快,但是总连接数到了2738就停止了。

1
2
3
4
5
#worker_rlimit_nofile  65535;
events {
use epoll;
worker_connections 1024;
}

这种情况,创建连接的速度非常慢,几分钟了才创建不到1000个连接。

结论:

worker_rlimit_nofile 影响总连接数

worker_connections 影响连接创建的速度

我对比了线上的正式服务器,线上这2个参数的值,都是设置为的65535.

常见问题

java.io.IOException: 远程主机强迫关闭了一个现有的连接。

不知道是我误关闭了浏览器,还是因为系统内存不足,自动关闭了浏览器,此时发现控制台报了2次这个错误:

java.io.IOException: 远程主机强迫关闭了一个现有的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:197)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
at org.apache.tomcat.util.net.NioChannel.read(NioChannel.java:147)
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1260)
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1196)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:72)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

将浏览器之前开启的2个标签页的连接给关掉了。

经测试,仅在浏览器异常关闭的情况下才会出现这个报错;正常关闭不会报错。

不过他只关闭这两个连接,剩下的连接不受影响,仍然可以正常使用。