使用四種框架分別實現1百萬websocket常連接的服務器

事實上,最近我又增加了幾個框架,現在包括 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 =4096873804161536
net.ipv4.tcp_rmem =4096873804161536
net.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 1000000
root hard nofile 1000000
root 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-nr
163201513506

其中第一個數表示當前系統已分配使用的打開文件描述符數,第二個數爲分配後已釋放的(目前已不再使用),第三個數等於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.0up
ifconfig eth0:1192.168.77.11netmask255.255.255.0up
ifconfig eth0:2192.168.77.12netmask255.255.255.0up
ifconfig eth0:3192.168.77.13netmask255.255.255.0up
ifconfig eth0:4192.168.77.14netmask255.255.255.0up
ifconfig eth0:5192.168.77.15netmask255.255.255.0up
ifconfig eth0:6192.168.77.16netmask255.255.255.0up
ifconfig eth0:7192.168.77.17netmask255.255.255.0up
ifconfig eth0:8192.168.77.18netmask255.255.255.0up
ifconfig eth0:9192.168.77.19netmask255.255.255.0up
ifconfig eth0:10192.168.77.20netmask255.255.255.0up
ifconfig eth0:11192.168.77.21netmask255.255.255.0up
ifconfig eth0:12192.168.77.22netmask255.255.255.0up
ifconfig eth0:13192.168.77.23netmask255.255.255.0up
ifconfig eth0:14192.168.77.24netmask255.255.255.0up
ifconfig eth0:15192.168.77.25netmask255.255.255.0up
ifconfig eth0:16192.168.77.26netmask255.255.255.0up
ifconfig eth0:17192.168.77.27netmask255.255.255.0up
ifconfig 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 =2000000
fs.nr_open =2000000
net.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 =2000000
fs.nr_open =2000000
net.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 -m
Total:1200231(kernel1200245)
TCP:1200006(estab1200002, closed0, orphaned0, synrecv0, timewait0/0), ports4

Transport Total IP IPv6
* 1200245- -
RAW 000
UDP 110
TCP 120000612000060
INET 120000712000070
FRAG 000

 total used free shared buffers cached
Mem:30074154321464109254
-/+ buffers/cache: 1516714906
Swap:8150815
  • 每分鐘給所有的120萬個websocket發送一條消息,消息內容爲當前的服務器的時間。這裏發送顯示是單線程發送,服務器發送完120萬個總用時15秒左右。
02:15:43.307[pool-1-thread-1]INFOcom.colobu.webtest.netty.WebServer$-sendmsgtochannelsforc4453a26-bca6-42b6-b29b-43653767f9fc
02: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 csw
00100000| 0 0 | 60B 540B| 0 0 | 224 440
00100000| 0 0 | 60B 870B| 0 0 | 192 382
00100000| 0 0 | 59k 74k| 0 0 |2306 2166
2787004| 0 0 |4998k 6134k| 0 0 | 169k 140k
1787005| 0 0 |4996k 6132k| 0 0 | 174k 140k
1787005| 0 0 |4972k 6102k| 0 0 | 176k 140k
1787005| 0 0 |5095k 6253k| 0 0 | 178k 142k
2787005| 0 0 |5238k 6428k| 0 0 | 179k 144k
1787005| 0 24k|4611k 5660k| 0 0 | 166k 129k
1787005| 0 0 |5083k 6238k| 0 0 | 175k 142k
1787005| 0 0 |5277k 6477k| 0 0 | 179k 146k
1787005| 0 0 |5297k 6500k| 0 0 | 179k 146k
1787005| 0 0 |5383k 6607k| 0 0 | 180k 148k
1787005| 0 0 |5504k 6756k| 0 0 | 184k 152k
1787005| 0 48k|5584k 6854k| 0 0 | 183k 152k
1787005| 0 0 |5585k 6855k| 0 0 | 183k 153k
1787005| 0 0 |5589k 6859k| 0 0 | 184k 153k
1591003| 0 0 |4073k 4999k| 0 0 | 135k 110k
00100000| 0 32k| 60B 390B| 0 0 |4822 424

客戶端(一共20個,這裏選取其中一個查看它的指標)。每個客戶端保持6萬個連接。每個消息從服務器發送到客戶端接收到總用時平均633毫秒,而且標準差很小,每個連接用時差不多。

Active WebSockets foreb810c24-8565-43ea-bc27-9a0b2c910ca4
count=60000
WebSocket Errors foreb810c24-8565-43ea-bc27-9a0b2c910ca4
count=0

-- Histograms ------------------------------------------------------------------
Message latency foreb810c24-8565-43ea-bc27-9a0b2c910ca4
count=693831
min=627
max=735
 mean = 633.06
 stddev = 9.61
 median = 631.00
75% <=633.00
95% <=640.00
98% <=651.00
99% <=670.00
99.9% <=735.00

-- Meters ----------------------------------------------------------------------
Message Rate foreb810c24-8565-43ea-bc27-9a0b2c910ca4
count=693832
 mean rate = 32991.37events/minute
1-minute rate =60309.26events/minute
5-minute rate =53523.45events/minute
15-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 -m
Total:1200234(kernel1200251)
TCP:1200006(estab1200002, closed0, orphaned0, synrecv0, timewait0/0), ports4

Transport Total IP IPv6
* 1200251- -
RAW 000
UDP 110
TCP 120000612000060
INET 120000712000070
FRAG 000

 total used free shared buffers cached
Mem:30074223717703010259
-/+ buffers/cache: 221007973
Swap: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-6cdf3e6787bf
05/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 csw
74914003| 0 24k|6330k 20M| 0 0 | 20k 1696
70230006| 0 64k| 11M 58M| 0 0 | 18k 2526
75116007| 0 0 |9362k 66M| 0 0 | 24k 11k
8248006| 0 0 | 11M 35M| 0 0 | 24k 10k
85014001| 0 0 |8334k 12M| 0 0 | 44k 415
84015001| 0 0 |9109k 16M| 0 0 | 36k 425
81019000| 0 24k| 919k 858k| 0 0 | 23k 629
76023000| 0 0 | 151k 185k| 0 0 | 18k 1075

客戶端(一共20個,這裏選取其中一個查看它的指標)。每個客戶端保持6萬個連接。每個消息從服務器發送到客戶端接收到總用時平均1412毫秒,而且標準差較大,每個連接用時差別較大。

Active WebSockets for 6674c9d8-24c6-4e77-9fc0-58afabe7436f
count =60000
WebSocket Errors for 6674c9d8-24c6-4e77-9fc0-58afabe7436f
count =0

-- Histograms ------------------------------------------------------------------
Message latency for 6674c9d8-24c6-4e77-9fc0-58afabe7436f
count =454157
min =716
max =9297
mean =1412.77
stddev =1102.64
median =991.00
75% <=1449.00
95% <=4136.00
98% <=4951.00
99% <=5308.00
99.9% <=8854.00

-- Meters ----------------------------------------------------------------------
Message Rate for 6674c9d8-24c6-4e77-9fc0-58afabe7436f
count =454244
 mean rate =18821.51events/minute
1-minuterate =67705.18events/minute
5-minuterate =49917.79events/minute
15-minuterate =24355.57events/minute

Undertow

  • 建立120萬個連接,不發送消息,輕輕鬆鬆達到。內存佔用較少,還剩餘11G內存。
[root@colobu ~]# ss -s; free -m
Total:1200234(kernel1200240)
TCP:1200006(estab1200002, closed0, orphaned0, synrecv0, timewait0/0), ports4

Transport Total IP IPv6
* 1200240- -
RAW 000
UDP 110
TCP 120000612000060
INET 120000712000070
FRAG 000

 total used free shared buffers cached
Mem:300741849711576010286
-/+ buffers/cache: 1820011873
Swap:8150815
  • 每分鐘給所有的120萬個websocket發送一條消息,消息內容爲當前的服務器的時間。

    羣發玩一次大約需要15秒。

03:19:31.154[pool-1-thread-1]INFOc.colobu.webtest.undertow.WebServer$-sendmsgtochannelsford9b450da-2631-42bc-a802-44285f63a62d
03: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 = 60000
WebSocket 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.00
75% <= 672.00
95% <= 678.00
98% <= 684.00
99% <= 690.00
99.9% <= 776.00

-- Meters ----------------------------------------------------------------------
Message Rate forb2e95e8d-b17a-4cfa-94d5-e70832034d4d
 count = 460813
 mean rate = 27065.85events/minute
1-minute rate =69271.67events/minute
5-minute rate =48641.78events/minute
15-minute rate =24128.67events/minute
Setup Rate forb2e95e8d-b17a-4cfa-94d5-e70832034d4d

node.js

node.js不是我要考慮的框架,列在這裏只是作爲參考。性能也不錯。

Active WebSockets for 537c7f0d-e58b-4996-b29e-098fe2682dcf
count =60000
WebSocket Errors for 537c7f0d-e58b-4996-b29e-098fe2682dcf
count =0

-- Histograms ------------------------------------------------------------------
Message latency for 537c7f0d-e58b-4996-b29e-098fe2682dcf
count =180000
min =808
max =847
mean =812.10
stddev =1.95
median =812.00
75% <=812.00
95% <=813.00
98% <=814.00
99% <=815.00
99.9% <=847.00

-- Meters ----------------------------------------------------------------------
Message Rate for 537c7f0d-e58b-4996-b29e-098fe2682dcf
count =180000
 mean rate =7191.98events/minute
1-minuterate =10372.33events/minute
5-minuterate =16425.78events/minute
15-minuterate =9080.53events/minute

參考文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章