天天看點

Websocket 研究 / Nodejs 子產品選型對比

導語 對Websocket的基礎原理研究,并在nodejs的WebSocket庫中進行選型對比,選出最适合我們的庫。本文分為兩章,第一張對WebSocket基礎原理進行研究,第二章将從Nodejs庫中選出最适合的WebSocket庫。
Websocket 研究 / Nodejs 子產品選型對比

WebSocket連接配接本質上是TCP連接配接,在網頁打開後通過http協定握手之後建立長連接配接。真正實作了Web的實時通信,使B/S模式具備了C/S模式的實時通信能力

Websocket 研究 / Nodejs 子產品選型對比

分為三個階段:

第一階段:由用戶端發起的握手階段,握手後建立連接配接

第二階段:資料交換,用戶端與服務端可以互相主動發送消息

第三階段:關閉連接配接,可以由任意一端發起關閉的指令

HTTP request method 必須是GET,協定應不小于1.1

Upgrade,并且其值為 websocket;

Connection,并且其值為Upgrade;

Sec-WebSocket-Key,其值采用base64編碼的随機16位元組長的字元序列;

Origin,伺服器可以從Origin決定是否接受該WebSocket連接配接;

Sec-webSocket-Version,目前值必須是13;握手響應

首行傳回的是HTTP/1.1協定版本和狀态碼101,表示變換協定(Switching Protocol)

Upgrade,其值為 websocket;

Connection,其值為Upgrade;

Sec-WebSocket-Accept,加密處理後的握手Key消息體組成

WebSocket的消息并非沒有額外資訊,除了業務資料以外,消息體也包含一些額外資訊。隻不過相對http的頭會小很多,一般隻有6個bytes

Websocket 研究 / Nodejs 子產品選型對比

FIN:1 bit

訓示這個是消息的最後片段。第一個片段可能也是最後的片段。

RSV1, RSV2, RSV3: 每個1 bit

必須是0,除非一個擴充協商為非零值定義含義。如果收到一個非零值且沒有協商的擴充定義這個非零值的含義,接收端點必須失敗WebSokcket連接配接。

Opcode: 4 bits

定義了“負載資料”的解釋。如果收到一個未知的操作碼,接收端點必須失敗WebSocket連接配接。定義了以下值。

%x0 代表一個繼續幀

%x1 代表一個文本幀

%x2 代表一個二進制幀

%x3-7 保留用于未來的非控制幀

%x8 代表連接配接關閉

%x9 代表ping

%xA 代表pong

%xB-F 保留用于未來的控制幀

Mask: 1 bit

定義是否“負載資料”是掩碼的。如果設定為1,一個掩碼鍵出現在masking-key,且這個是用于根據5.3節解掩碼(unmask)“負載資料”。從用戶端發送到伺服器的所有幀有這個位設定為1。

Payload length: 7 bits, 7+16 bits, 或者 7+64 bits

“負載資料”的長度,以位元組為機關:如果0-125,這是負載長度。如果126,之後的兩位元組解釋為一個16位的無符号整數是負載長度。如果127,之後的8位元組解釋為一個64位的無符号整數(最高有效位必須是0)是負載長度。多位元組長度數量以網絡位元組順序來表示。注意,在所有情況下,最小數量的位元組必須用于編碼長度,例如,一個124位元組長的字元串的長度不能被編碼為序列126,0,124。負載長度是“擴充資料”長度+“應用資料”長度。“擴充資料”長度可能是零,在這種情況下,負載長度是“應用資料”長度。

Masking-key: 0 or 4 bytes

用戶端發送到伺服器的所有幀通過一個包含在幀中的32位值來掩碼。如果mask位設定為1,則該字段存在,如果mask位設定為0,則該字段缺失。詳細資訊請參見5.3節 用戶端到伺服器掩碼。

Payload data: (x+y) bytes

“負載資料”定義為“擴充資料”連接配接“應用資料”。

Extension data: x bytes

“擴充資料”是0位元組除非已經協商了一個擴充。任何擴充必須指定“擴充資料”的長度,或長度是如何計算的,以及擴充如何使用必須在打開階段握手期間協商。 如果存在,“擴充資料”包含在總負載長度中。

Application data: y bytes

任意的“應用資料”,占用“擴充資料”之後幀的剩餘部分。“應用資料”的長度等于負載長度減去“擴充資料”長度。

Websocket 研究 / Nodejs 子產品選型對比

FIN + RSV1 + RSV2 + RSV3 + Opcode + Mask + Payload length + Masking-key = 業務資料以外的消息大小

1bit + 1bit + 1bit + 1bit + 4bit + 1bit + 7bit + 4bytes = 6bytes

以發送JSON字元串 {“req”:”123”} 為例,字元串本身13 bytes

通過http發送的話,http消息總大小 523+13

通過WebSocket發送的話,消息總大小是 6+13

由于工作原因,主要用Nodejs進行開發,是以隻對比Nodejs實作的WebSocket庫

GitHub上面,用nodejs實作的WebSocket庫非常多,我挑選了幾個靠前的庫進行對比

websockets/ws

theturtle32/WebSocket-Node

faye/faye-websocket-node

socketio/socket.io本地Windows環境 Ajax vs WebSocket

在本地Windows環境,對比Ajax與WebSocket發送消息的耗時。可以看到WebSocket的耗時遠遠低于Ajax

Websocket 研究 / Nodejs 子產品選型對比

在本地Windows環境,處理不同消息大小的耗時對比。

測試結果: websocket-node < faye < ws < socket.io

Websocket 研究 / Nodejs 子產品選型對比

因為本地Windows環境與生産環境并不一樣,是以上面的資料僅作Windows環境參考。因為下面在生産環境進行對比後,資料會有較大差異

以下生産環境測試,都是在2G記憶體、10個ecu環境下進行的測試對比

這個測試與上一個Windows測試是一樣的,但結果完全不同。ws表現最好

測試結果:ws< socket.io < websocket-node < faye < ajax

Websocket 研究 / Nodejs 子產品選型對比

使用同樣大小的消息,對服務發起大量的請求。測試服務的記憶體消耗。socket.io/ws/websocket-node 表現都不錯,比較穩定。faye表現最差,占用記憶體高。

測試結果:socket.io < ws < websocket-node < faye

Websocket 研究 / Nodejs 子產品選型對比

使用同樣大小的消息,對服務發起大量的請求。測試服務的CPU占用情況。socket.io表現最差,CPU占比很高。

測試結果:websocket-node = faye < ws < socket.io

Websocket 研究 / Nodejs 子產品選型對比

在2G記憶體的伺服器上,測試各個庫的最大連接配接數。最好的結果也是差異巨大。最好的ws是最差的socket.io的近三倍

測試結果:ws > websocket-node > faye > socket.io

Websocket 研究 / Nodejs 子產品選型對比

websocket-node 在連接配接數超過140000的時候,連接配接速度比較慢。伺服器沒響應,但之前的連接配接不會斷開

而faye和ws在到極限的時候,會出現異常。所有連接配接會斷開

socket.io 連接配接在20000左右 的時候,就非常慢了

測試最大連接配接數的時候,同時監控了記憶體和CPU的波動。

在記憶體方面,ws的增長最為平緩,而socket.io早早的攀升到了極限最後挂掉了

測試結果:ws < websocket-node < faye < socket.io

Websocket 研究 / Nodejs 子產品選型對比

在CPU方面,ws同樣保持穩定,占用比也非常低。

Websocket 研究 / Nodejs 子產品選型對比

按第一得分4,第二得3分,第三得2分,第四得1分計算各個庫的得分情況

得分

ws

21

websocket-node

17

faye

11

socket.io

ws表現最好簡單易用,連接配接數最大,記憶體和CPU控制的穩定。缺點是在到達最大連接配接數極限之後,會斷開所有連接配接