使用四种框架分别实现1百万websocket常连接的服务器

来源:互联网 发布:泳道图 软件 编辑:程序博客网 时间:2024/05/18 03:02

事实上,最近我又增加了几个框架,现在包括 Netty, Undertow, Jetty, Spray, Vert.x, Grizzly 和 Node.js其中框架。

测试数据可以看下一篇文章: 七种WebSocket框架的性能比较

著名的 C10K 问题提出的时候, 正是 2001 年。这篇文章可以说是高性能服务器开发的一个标志性文档,它讨论的就是单机为1万个连接提供服务这个问题,当时因为硬件和软件的限制,单机1万还是一个非常值得挑战的目标。但是时光荏苒,随着硬件和软件的飞速发展,单机1万的目标已经变成了最简单不过的事情。现在用任何一种主流语言都能提供单机1万的并发处理的能力。所以现在目标早已提高了100倍,变成C1000k,也就是一台服务器为100万连接提供服务。在2010年,2011年已经看到一些实现C1000K的文章了,所以在2015年,实现C1000K应该不是一件困难的事情。

本文是我在实践过程中的记录,我的目标是使用spran-websocket,netty, undertow和node.js四种框架分别实现C1000K的服务器,看看这几个框架实现的难以程度,性能如何。开发语言为Scala和Javascript。

当然,谈起性能,我们还必须谈到每秒每个连接有多少个请求,也就是RPS数,还要考虑每条消息的大小。

一般来说,我们会选取一个百分比,比如每秒20%的连接会收发消息。我的需求是服务器只是 push ,客户端不会主动发送消息。 一般每一分钟会为这一百万群发一条消息。

所以实现的测试工具每个client建立60000个websocket连接,一共二十个client。实际不可能使用20台机器,我使用了两台AWS C3.2xlarge(8核16G)服务器作为客户端机。每台机器10个客户端。

服务器每1分钟群发一条消息。消息内容很简单,只是服务器的当天时间。

最近看到360用Go实现的消息推送系统,下面是他们的数据:

目前360消息推送系统服务于50+内部产品,万款开发平台App,实时长连接数亿量级,日独数十亿量级,1分钟内可以实现亿量级广播,日下发峰值百亿量级,400台物理机,3000多个实例分布在9个独立集群中,每个集群跨国内外近10个IDC。

四个服务器的代码和Client测试工具代码可以在 github 上下载。 (其实不止四种框架了,现在包括Netty, Undertow, Jetty, Spray-websocket, Vert.x, Grizzly 和 Node.js 七种框架的实现)

测试下来可以看到每种服务器都能轻松达到同时120万的websocket活动连接,只是资源占用和事务处理时间有差别。120万只是保守数据,在这么多连接情况下服务器依然很轻松,下一步我会进行C2000K的测试。

在测试之前我们需要对服务器/客户机的一些参数进行调优。

服务器的参数调优

一般会修改两个文件, /etc/sysctl.conf 和 /etc/security/limits.conf, 用来配置TCP/IP参数和最大文件描述符。

TCP/IP参数配置

修改文件 /etc/sysctl.conf ,配置网络参数。

net.ipv4.tcp_wmem =4096873804161536net.ipv4.tcp_rmem =4096873804161536net.ipv4.tcp_mem =78643220971523145728

数值根据需求进行调整。更多的参数可以看以前整理的一篇文章: Linux TCP/IP 协议栈调优 。

执行 /sbin/sysctl -p 即时生效。

最大文件描述符

Linux内核本身有文件描述符最大值的限制,你可以根据需要更改:

  • 系统最大打开文件描述符数:/proc/sys/fs/file-max
    1. 临时性设置: echo 1000000 > /proc/sys/fs/file-max
    2. 永久设置:修改 /etc/sysctl.conf 文件,增加 fs.file-max = 1000000
  • 进程最大打开文件描述符数 
    使用 ulimit -n 查看当前设置。使用 ulimit -n 1000000 进行临时性设置。 
    要想永久生效,你可以修改 /etc/security/limits.conf 文件,增加下面的行:
* hard nofile 1000000* soft nofile 1000000root hard nofile 1000000root soft nofile 1000000

还有一点要注意的就是hard limit不能大于 /proc/sys/fs/nr_open ,因此有时你也需要修改nr_open的值。

执行 echo 2000000 > /proc/sys/fs/nr_open

查看当前系统使用的打开文件描述符数,可以使用下面的命令:

[root@localhost ~]# cat /proc/sys/fs/file-nr163201513506

其中第一个数表示当前系统已分配使用的打开文件描述符数,第二个数为分配后已释放的(目前已不再使用),第三个数等于file-max。

总结一下:

  • 所有进程打开的文件描述符数不能超过/proc/sys/fs/file-max
  • 单个进程打开的文件描述符数不能超过user limit中nofile的soft limit
  • nofile的soft limit不能超过其hard limit
  • nofile的hard limit不能超过/proc/sys/fs/nr_open

应用运行时调优

  1. Java 应用内存调优

    服务器使用12G内存,吞吐率优先的垃圾回收器:

JAVA_OPTS="-Xms12G -Xmx12G -Xss1M -XX:+UseParallelGC"
  1. V8引擎
node --nouse-idle-notification --expose-gc --max-new-space-size=1024--max-new-space-size=2048--max-old-space-size=8192./webserver.js

OutOfMemory Killer

如果服务器本身内存不大,比如8G,在不到100万连接的情况下,你的服务器进程有可能出现"Killed"的问题。 运行 dmesg 可以看到

Outofmemory: Killprocess10375(java) score59orsacrifice child

这是Linux的OOM Killer主动杀死的。 开启oom-killer的话,在/proc/pid下对每个进程都会多出3个与oom打分调节相关的文件。临时对某个进程可以忽略oom-killer可以使用下面的方式:

echo -17 > /proc/$(pidof java)/oom_adj 

解决办法有多种,可以参看文章最后的参考文章,最好是换一个内存更大的机器。

客户端的参数调优

在一台系统上,连接到一个远程服务时的本地端口是有限的。根据TCP/IP协议,由于端口是16位整数,也就只能是0到 65535,而0到1023是预留端口,所以能分配的端口只是1024到65534,也就是64511个。也就是说,一台机器一个IP只能创建六万多个长连接。

要想达到更多的客户端连接,可以用更多的机器或者网卡,也可以使用虚拟IP来实现,比如下面的命令增加了19个IP地址,其中一个给服务器用,其它18个给client,这样

可以产生18 * 60000 = 1080000个连接。

ifconfigeth0:0192.168.77.10netmask255.255.255.0upifconfig eth0:1192.168.77.11netmask255.255.255.0upifconfig eth0:2192.168.77.12netmask255.255.255.0upifconfig eth0:3192.168.77.13netmask255.255.255.0upifconfig eth0:4192.168.77.14netmask255.255.255.0upifconfig eth0:5192.168.77.15netmask255.255.255.0upifconfig eth0:6192.168.77.16netmask255.255.255.0upifconfig eth0:7192.168.77.17netmask255.255.255.0upifconfig eth0:8192.168.77.18netmask255.255.255.0upifconfig eth0:9192.168.77.19netmask255.255.255.0upifconfig eth0:10192.168.77.20netmask255.255.255.0upifconfig eth0:11192.168.77.21netmask255.255.255.0upifconfig eth0:12192.168.77.22netmask255.255.255.0upifconfig eth0:13192.168.77.23netmask255.255.255.0upifconfig eth0:14192.168.77.24netmask255.255.255.0upifconfig eth0:15192.168.77.25netmask255.255.255.0upifconfig eth0:16192.168.77.26netmask255.255.255.0upifconfig eth0:17192.168.77.27netmask255.255.255.0upifconfig eth0:18192.168.77.28netmask255.255.255.0up

修改 /etc/sysctl.conf 文件:

net.ipv4.ip_local_port_range =1024 65535

执行 /sbin/sysctl -p 即时生效。

服务器测试

实际测试中我使用一台 AWS C3.4xlarge (16 cores, 32G memory) 作为应用服务器,两台 AWS C3.2xlarge (8 cores, 16G memory) 服务器作为客户端。

这两台机器作为测试客户端绰绰有余,每台客户端机器创建了十个内网虚拟IP, 每个IP创建60000个websocket连接。

客户端配置如下:

/etc/sysctl.conf 配置

fs.file-max =2000000fs.nr_open =2000000net.ipv4.ip_local_port_range =102465535

/etc/security/limits.conf 配置

* soft nofile 2000000* hard nofile 2000000* soft nproc 2000000* hard nproc 2000000

服务端配置如下:

/etc/sysctl.conf 配置

fs.file-max =2000000fs.nr_open =2000000net.ipv4.ip_local_port_range =102465535

/etc/security/limits.conf 配置

* soft nofile 2000000* hard nofile 2000000* soft nproc 2000000* hard nproc 2000000

Netty服务器

  • 建立120万个连接,不发送消息,轻轻松松达到。内存还剩14G未用。
[roocolobu ~]# ss -s; free -mTotal:1200231(kernel1200245)TCP:1200006(estab1200002, closed0, orphaned0, synrecv0, timewait0/0), ports4Transport Total IP IPv6* 1200245- -RAW 000UDP 110TCP 120000612000060INET 120000712000070FRAG 000 total used free shared buffers cachedMem:30074154321464109254-/+ buffers/cache: 1516714906Swap:8150815
  • 每分钟给所有的120万个websocket发送一条消息,消息内容为当前的服务器的时间。这里发送显示是单线程发送,服务器发送完120万个总用时15秒左右。
02:15:43.307[pool-1-thread-1]INFOcom.colobu.webtest.netty.WebServer$-sendmsgtochannelsforc4453a26-bca6-42b6-b29b-43653767f9fc02:15:57.190[pool-1-thread-1]INFOcom.colobu.webtest.netty.WebServer$-sent1200000channelsforc4453a26-bca6-42b6-b29b-43653767f9fc

发送时CPU使用率并不高,网络带宽占用基本在10M左右。

----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--usr sys idl wai hiq siq| read writ| recv send| in out | int csw00100000| 0 0 | 60B 540B| 0 0 | 224 44000100000| 0 0 | 60B 870B| 0 0 | 192 38200100000| 0 0 | 59k 74k| 0 0 |2306 21662787004| 0 0 |4998k 6134k| 0 0 | 169k 140k1787005| 0 0 |4996k 6132k| 0 0 | 174k 140k1787005| 0 0 |4972k 6102k| 0 0 | 176k 140k1787005| 0 0 |5095k 6253k| 0 0 | 178k 142k2787005| 0 0 |5238k 6428k| 0 0 | 179k 144k1787005| 0 24k|4611k 5660k| 0 0 | 166k 129k1787005| 0 0 |5083k 6238k| 0 0 | 175k 142k1787005| 0 0 |5277k 6477k| 0 0 | 179k 146k1787005| 0 0 |5297k 6500k| 0 0 | 179k 146k1787005| 0 0 |5383k 6607k| 0 0 | 180k 148k1787005| 0 0 |5504k 6756k| 0 0 | 184k 152k1787005| 0 48k|5584k 6854k| 0 0 | 183k 152k1787005| 0 0 |5585k 6855k| 0 0 | 183k 153k1787005| 0 0 |5589k 6859k| 0 0 | 184k 153k1591003| 0 0 |4073k 4999k| 0 0 | 135k 110k00100000| 0 32k| 60B 390B| 0 0 |4822 424

客户端(一共20个,这里选取其中一个查看它的指标)。每个客户端保持6万个连接。每个消息从服务器发送到客户端接收到总用时平均633毫秒,而且标准差很小,每个连接用时差不多。

Active WebSockets foreb810c24-8565-43ea-bc27-9a0b2c910ca4count=60000WebSocket Errors foreb810c24-8565-43ea-bc27-9a0b2c910ca4count=0-- Histograms ------------------------------------------------------------------Message latency foreb810c24-8565-43ea-bc27-9a0b2c910ca4count=693831min=627max=735 mean = 633.06 stddev = 9.61 median = 631.0075% <=633.0095% <=640.0098% <=651.0099% <=670.0099.9% <=735.00-- Meters ----------------------------------------------------------------------Message Rate foreb810c24-8565-43ea-bc27-9a0b2c910ca4count=693832 mean rate = 32991.37events/minute1-minute rate =60309.26events/minute5-minute rate =53523.45events/minute15-minute rate =31926.26events/minute

平均每个client的RPS = 1000, 总的RPS大约为 20000 requests /seconds.latency平均值为633 ms,最长735 ms,最短627ms。

Spray服务器

  • 建立120万个连接,不发送消息,轻轻松松达到。它的内存相对较高,内存还剩7G。
[root@colobu ~]# ss -s; free -mTotal:1200234(kernel1200251)TCP:1200006(estab1200002, closed0, orphaned0, synrecv0, timewait0/0), ports4Transport Total IP IPv6* 1200251- -RAW 000UDP 110TCP 120000612000060INET 120000712000070FRAG 000 total used free shared buffers cachedMem:30074223717703010259-/+ buffers/cache: 221007973Swap:8150815
  • 每分钟给所有的120万个websocket发送一条消息,消息内容为当前的服务器的时间。

    CPU使用较高,发送很快,带宽可以达到46M。群发完一次大约需要8秒左右。

05/2204:42:57.569INFO[ool-2-worker-15]c.c.w.s.WebServer- send msg to workers 。for8454e7d8-b8ca-4881-912b-6cdf3e6787bf05/2204:43:05.279INFO[ool-2-worker-15]c.c.w.s.WebServer- sent msg to workersfor8454e7d8-b8ca-4881-912b-6cdf3e6787bf. current workers:1200000
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--usr sys idl wai hiq siq| read writ| recv send| in out | int csw74914003| 0 24k|6330k 20M| 0 0 | 20k 169670230006| 0 64k| 11M 58M| 0 0 | 18k 252675116007| 0 0 |9362k 66M| 0 0 | 24k 11k8248006| 0 0 | 11M 35M| 0 0 | 24k 10k85014001| 0 0 |8334k 12M| 0 0 | 44k 41584015001| 0 0 |9109k 16M| 0 0 | 36k 42581019000| 0 24k| 919k 858k| 0 0 | 23k 62976023000| 0 0 | 151k 185k| 0 0 | 18k 1075

客户端(一共20个,这里选取其中一个查看它的指标)。每个客户端保持6万个连接。每个消息从服务器发送到客户端接收到总用时平均1412毫秒,而且标准差较大,每个连接用时差别较大。

Active WebSockets for 6674c9d8-24c6-4e77-9fc0-58afabe7436fcount =60000WebSocket Errors for 6674c9d8-24c6-4e77-9fc0-58afabe7436fcount =0-- Histograms ------------------------------------------------------------------Message latency for 6674c9d8-24c6-4e77-9fc0-58afabe7436fcount =454157min =716max =9297mean =1412.77stddev =1102.64median =991.0075% <=1449.0095% <=4136.0098% <=4951.0099% <=5308.0099.9% <=8854.00-- Meters ----------------------------------------------------------------------Message Rate for 6674c9d8-24c6-4e77-9fc0-58afabe7436fcount =454244 mean rate =18821.51events/minute1-minuterate =67705.18events/minute5-minuterate =49917.79events/minute15-minuterate =24355.57events/minute

Undertow

  • 建立120万个连接,不发送消息,轻轻松松达到。内存占用较少,还剩余11G内存。
[root@colobu ~]# ss -s; free -mTotal:1200234(kernel1200240)TCP:1200006(estab1200002, closed0, orphaned0, synrecv0, timewait0/0), ports4Transport Total IP IPv6* 1200240- -RAW 000UDP 110TCP 120000612000060INET 120000712000070FRAG 000 total used free shared buffers cachedMem:300741849711576010286-/+ buffers/cache: 1820011873Swap:8150815
  • 每分钟给所有的120万个websocket发送一条消息,消息内容为当前的服务器的时间。

    群发玩一次大约需要15秒。

03:19:31.154[pool-1-thread-1]INFOc.colobu.webtest.undertow.WebServer$-sendmsgtochannelsford9b450da-2631-42bc-a802-44285f63a62d03:19:46.755[pool-1-thread-1]INFOc.colobu.webtest.undertow.WebServer$-sent1200000channelsford9b450da-2631-42bc-a802-44285f63a62d

客户端(一共20个,这里选取其中一个查看它的指标)。每个客户端保持6万个连接。每个消息从服务器发送到客户端接收到总用时平均672毫秒,而且标准差较小,每个连接用时差别不大。

Active WebSockets forb2e95e8d-b17a-4cfa-94d5-e70832034d4d count = 60000WebSocket Errors forb2e95e8d-b17a-4cfa-94d5-e70832034d4d count = 0-- Histograms ------------------------------------------------------------------Message latency forb2e95e8d-b17a-4cfa-94d5-e70832034d4d count = 460800 min = 667 max = 781 mean = 672.12 stddev = 5.90 median = 671.0075% <= 672.0095% <= 678.0098% <= 684.0099% <= 690.0099.9% <= 776.00-- Meters ----------------------------------------------------------------------Message Rate forb2e95e8d-b17a-4cfa-94d5-e70832034d4d count = 460813 mean rate = 27065.85events/minute1-minute rate =69271.67events/minute5-minute rate =48641.78events/minute15-minute rate =24128.67events/minuteSetup Rate forb2e95e8d-b17a-4cfa-94d5-e70832034d4d

node.js

node.js不是我要考虑的框架,列在这里只是作为参考。性能也不错。

Active WebSockets for 537c7f0d-e58b-4996-b29e-098fe2682dcfcount =60000WebSocket Errors for 537c7f0d-e58b-4996-b29e-098fe2682dcfcount =0-- Histograms ------------------------------------------------------------------Message latency for 537c7f0d-e58b-4996-b29e-098fe2682dcfcount =180000min =808max =847mean =812.10stddev =1.95median =812.0075% <=812.0095% <=813.0098% <=814.0099% <=815.0099.9% <=847.00-- Meters ----------------------------------------------------------------------Message Rate for 537c7f0d-e58b-4996-b29e-098fe2682dcfcount =180000 mean rate =7191.98events/minute1-minuterate =10372.33events/minute5-minuterate =16425.78events/minute15-minuterate =9080.53events/minute

参考文档

0 0
原创粉丝点击