记一次Socket.IO长链服务的性能压测

Posted 网易云信

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次Socket.IO长链服务的性能压测相关的知识,希望对你有一定的参考价值。

网易云信IM系统中的Web版使用了Socket.IO实现浏览器环境下的长链服务;区别于常规的长链服务,为该服务的压测提出了一些新的挑战,本文总结了测试过程中的一些收获供参考。

Part1 测试工具选项

一、工具选型

  • Gatling

  • Node.js

  • JMeter

  • Java WebSocket

这些工具都是可以支持WebSocket协议的工具,主要从以下几个方面进行对比:

  • 上手难易程度

  • 测试资源开销

  • 和性能测试平台结合的难易程度

二、对比过程

对比场景

(1)5000个用户,每秒钟发送100个登录请求,并保持连接

(2)5000个用户,每隔5s发送一条点对点消息,即发送消息频率1000

工具使用过程

1. Gatling

工具简介:Gatling是一款基于Scala 开发的高性能服务器性能测试工具,它主要用于对服务器进行负载测试,并分析和测量服务器的各种性能指标。

工具语言:Scala语言

工具官网

http://gatling.io/docs/2.0.0-RC2/index.html#

相关代码

遇到的问题

(1)scale语言比较陌生,脚本编写时困难较多,目前暂未实现隔一定时间,即发心跳又发消息的场景;

(2)无法和性能测试平台结合,系统整合成本比较高;

2. Node.js + Socket.IO

工具简介:

Node.js是一个基于Chrome V8引擎的javascript运行环境。Node.js使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效;而Socket.IO是一个基于Node.js架构体系的且原生支持WebSocket协议,可用作实时通信的软件包。Socket.IO为跨浏览器构建实时应用提供了完整的封装,且完全由JavaScript实现,语言更容易理解。

工具语言:JavaScript

工具官网:

• http://socket.io/docs/

• http://nodejs.cn/

相关代码:

记一次Socket.IO长链服务的性能压测

遇到问题:

(1)多节点并发分布式控制

(2)整合到现有的性能测试平台

3. JMeter

工具简介:

JMeter是比较常见的性能测试开源工具,支持多种协议并且可以自定义java请求,更加灵活。

工具语言:

(1)Java

(2)JMeter xml脚本文件

工具官网:

http://jmeter.apache.org/usermanual/get-started.html

https://blog.flood.io/socket-io-and-websockets-with-gatling/

相关代码:

记一次Socket.IO长链服务的性能压测

遇到问题:

JMeter的工具本上是同步的,如果建立1万个连接的话,需要启动1万个线程处理,因此不适合测试高并发长连接的场景。

4. Jetty WebSocket Client

工具简介:

Jetty提供了功能更强的WebSocket API,使用一个公共的核心API供WebSocket的服务端和客户端使用。

工具语言: Java

工具官网:

http://www.eclipse.org/jetty/

相关代码:

记一次Socket.IO长链服务的性能压测

遇到问题:

需要自己写并发控制

三、对比结果总结

因为用来做性能压测,所以考虑到压测客户端需要实现高并发请求,选择Gatling和Socket.IO做了简单的对比测试。

记一次Socket.IO长链服务的性能压测

Part2 长链服务端性能问题的定位

如第一部分所述,我们最终选择用Socket.IO实现了并发压测的长链客户端,下面简单说明下长链服务器端测试的一些收获;

首先,简单描述下被测试服务器的功能

1. 和前端Socket.IO客户端保持长连接;

2. 解析前端JSON结构的数据包,并作二次封装后抓发给后端APP服务,并且将后端APP服务转发过来的响应包转换成为socketio可以识别的json数据包,并发送给客户端

为此,我们确定该服务主要的测试点为

1. 测试服务器可以支撑的最大连接数:该测试点涉及到了TCP连接,以及我们需要注意哪些参数,才可以保证用户建立的连接数目不会因为限制而达不到目标。

2. 每秒钟可以解析的包的数量:该测试点涉及到了网络流量,如果已经达到了网络流量的峰值时,会出现什么样的问题。

以下是这次测试过程中遇到的一系列问题:

问题1:最大连接数只能达到65K+

65535,对于程序员来说,这是一个很敏感的数字,因为一台服务器,限制的最大端口号即为65535,所以出现这个问题后:

  • 第一反应:端口号不够用了。 分析认为Link服务作为服务端,对外提供的服务端口仅有一个,所以不存在服务端端口号不够用的情况;

  • 第二反应:句柄数不够用了。文件句柄数相当于文件的标识符,建立一条socket链接,同样会使用一个文件句柄,而系统默认的单个进程使用的文件句柄为1024;

对于这种需要保持大量连接的服务来说,一般情况下都是需要修改文件句柄数的,文件句柄数修改的方法如下:

 A)查看单个进程使用的最大文件句柄数的方法:

记一次Socket.IO长链服务的性能压测

B)查看当前进程打开了多少个文件句柄呢:

记一次Socket.IO长链服务的性能压测

C)修改Linux的最大文件句柄数限制的方法:

  1)ulimit -n 65535

在当前session有效,用户退出或者系统重新后恢复默认值

  2)修改用户下的.profile文件:在.profile文件中添加:ulimit -n 65535

  只对当前用户有效

 3)修改文件/etc/security/limits.conf,在文件中添加:

(立即生效-当前session中运行ulimit -a命令无法显示)

记一次Socket.IO长链服务的性能压测

4)修改文件/etc/sysctl.conf添加:

记一次Socket.IO长链服务的性能压测

运行命令:/sbin/sysctl -p 使配置生效

但是修改文件句柄的限制后,该问题依然没有得到解决。

这个时候想到可以使用dmesg查看系统日志,dmesg命令可以显示Linux内核的环形缓冲区信息,可以从中获得诸如系统架构、CPU和挂载的硬件,RAM等运行级别的大量系统信息,因此dmesg命令在设备故障的诊断方面是非常重要的。通过dmesg,查看到了如下问题:

记一次Socket.IO长链服务的性能压测

从上面的信息可见conntrack表满了,那么conntrack表是做什么的?

nf_conntrack/ip_conntrack用来跟踪连接条目,会使用一个哈希表来记录 established 的记录

nf_conntrack 在 2.6.15 被引入,而 ip_conntrack 在 2.6.22 被移除,如果该哈希表满了dmesg命令就会出现:

nf_conntrack: table full, dropping packet

如何修改conntrack表的大小

记一次Socket.IO长链服务的性能压测

到此为止,这个问题我们算是解决了,总结下,遇到的网络相关的知识点包括 文件句柄 和conntrack表。

问题2:在某些用例场景下,稳定连接只能建立3800+

这个问题的前提是某些时候,也就是说偶现的,这种情况基本上可以排除是应用程序内部的问题。那对于这个问题我们使用了哪些定位手段呢?

1. watch和netstat 两个命令

  • watch -d 定期查看一些信息,默认是2s;

  • netstat -st | grep ignored 查看TCP连接的一些统计信息;

可见有SYNs to LISTEN sockets ignored在不停增加,这表明收到连接建立过程中的三次握手的ACK包,但是因各种原因(包括accept队列满) 创建socket失败;

2. ss -ln : 查看进程对应的backlog的使用情况

backlog:简单理解来说,连接已经在TCP层建立成功(完成了三次握手的过程)但是还没有被应用程序所接受,这种情况下,该连接会存放到backlog这样一个缓冲队列内

通过上面这个命令可以看到应用程序的backlog队列已经满了,但是即便把这个值调整为1024,该队列还是会满的。

到这儿,我们的定位过程陷入了僵局,下一步该怎么定位呢?这期间我们查看了内存信息,CPU使用情况等,均没有找对地方;但是在定位过程中,我们发现,有时候JStack、JProfiler等工具都无法连上该服务了。

突然想到文件句柄是不是满了?又执行了以下几条命令:

记一次Socket.IO长链服务的性能压测

/proc/{pid}这个目录下了对应了所有在运行的进程的相关信息,

通过查看/proc/{pid}/fd目录下的个数我们可以看到当前进程使用的文件句柄数有多少。

通过查看limits文件里面的值,可以看到系统对该进程的一些限制,不幸的是,出现这个问题的时候,max open files这个值被限制为了4096。

调整该值之后最终解决了这个问题

问题3:大量的连接建立不成功

当我们解决了一个又一个的问题后,突然发现高压力下存在大量的连接建立不成功的问题,主要表现在:

原来:每秒钟500个连接建立的请求时,5w个用户都可以建立成功;

现在:每秒钟500个连接建立的请求时,5w个用户只有4w左右的连接可以建立成功;

这个问题也花费了不短的时间,我们使用了各种命令查看各种网络指标

  1. 通过netstat查看到send-q中有大量的消息堆积;

  2. 通过sar查看到有大量的重传;

总结起来还是TcpDump抓包比较效果更好,从测试的开始,我们就只抓这一条链路上的包,且发送方和接收方,双方都抓包:

记一次Socket.IO长链服务的性能压测

然后通过Wireshark分析,可以看到

  • 发送方/客户端:存在一定时间段发送出大量的重传包

  • 接收方/服务端:客户端发送大量重传包的这个过程中,什么都没有收到

这基本说明网络有问题

然后我们通过netperf测试了下网络带宽的情况,发现这两台机器之间的网络上限只有21Mb,而且测试过程中的网络请求量是超过这个值的。由于是在云主机环境上,通过咨询云网络相关的同事发现,问题原因是因为云主机之间的网络QoS开启限制导致的,限制了每台云主机之间的网络带宽最高位21Mb,超过这个值则会被丢包。

记一次Socket.IO长链服务的性能压测


TCP的链接状态图


最后总结下在定位类似的网络问题中一些常规的命令、工具和关注的方向

可以使用以下命令

记一次Socket.IO长链服务的性能压测

推荐使用以下工具

重点关注以下指标


点击下方“阅读原文”,阅读更多技术干货

↓↓

以上是关于记一次Socket.IO长链服务的性能压测的主要内容,如果未能解决你的问题,请参考以下文章

记一次 JMeter 压测 HTTPS 性能问题

记一次压测中Mysql数据库异常分析过程

记一次drools5的性能优化过程

生产环境 压测

记一次cephfs插件(csi)使用的坑

后端服务性能压测实践