引言
ASP.NET Core SignalR 作為微軟官方出品的一個實時Web中間件功能庫,可以使服務器端代碼可以將內容立即推送到客戶端。
SignalR supports the following techniques for handling real-time communication (in order of graceful fallback):
- WebSockets
- Server-Sent Events
- Long Polling
SignalR automatically chooses the best transport method that is within the capabilities of the server and client.
背景
在使用jmeter對自己搭建的聊天服務(消息轉發服務)進行壓力測試的時候,發現和傳統的websocket連接測試過程不同,比如websocket連接地址是什么,端口是什么,這些都不是signalr去暴露出來的,因此進入了深一步的研究。
通俗來講,SignalR只是一個實時Web的中間件,它支持的通訊模式有WebSockets\SSE\Long Polling ,目前我們只考慮最優的websocket模式,那么它的websocket地址是什么呢?
因為我是將SignalR中間件寄宿於Web MVC中,然后部署在Linux下,所以這里websocket的地址就是 127.0.0.1/chatHub ,后面的名稱,具體的路由地址,看你再starup.cs中如何映射SignalR請求地址和后台中的Hub名稱。
過程分析
通過web端監控,發現連接客戶端連接SignalR會執行兩個步驟
negotiate
返回的結果,注意看下這個connectionToken ,這個token會作為 一個參數在接下來的chatHub中去請求服務器。
{
"negotiateVersion": 1,
"connectionId": "eZuczhINi9cewADWJO4kXA",
"connectionToken": "SZyT2pHeOXXNaCrveIJx5A",
"availableTransports": [
{
"transport": "WebSockets",
"transferFormats": [
"Text",
"Binary"
]
},
{
"transport": "ServerSentEvents",
"transferFormats": [
"Text"
]
},
{
"transport": "LongPolling",
"transferFormats": [
"Text",
"Binary"
]
}
]
}
從源碼文檔的描述中可以看出,如果在請求的時候,沒有發送negotiateVersion這個參數,它會默認為0,且此時返回的數據中是沒有connectionToken這個參數的。
並且在連接websocket的時候,如果對id這個參數賦值的話,如果它是錯誤的,會提示你無法連接,但是如果你不傳這個id,它其實是可以連接上的。
連接websocket-->chatHub
WebSockets傳輸是唯一的,因為它是全雙工的,並且可以在單個操作中建立持久連接。結果,不需要客戶端使用該POST [endpoint-base]/negotiate
請求來預先建立連接。它還在其自己的幀元數據中包含所有必需的元數據。
通過建立與WebSocket的連接來激活WebSocket傳輸[endpoint-base]
。該可選的 id
查詢字符串值被用來識別附加到連接。如果沒有id
查詢字符串值,則建立新連接。如果指定了參數,但沒有與指定ID值的連接,404 Not Found
則返回響應。收到此請求后,將建立連接,並且服務器將101 Switching Protocols
立即通過WebSocket升級()進行響應,以准備發送/接收幀。WebSocket OpCode字段用於指示幀的類型(文本或二進制)。
如果已經存在與端點連接關聯的WebSocket連接,則不允許建立第二個WebSocket連接,並且將失敗,並顯示409 Conflict
狀態代碼。
建立連接時的錯誤通過返回500 Server Error
狀態碼作為對升級請求的響應來處理。這包括初始化EndPoint類型的錯誤。未處理的應用程序錯誤會觸發WebSocketClose
框架,其原因碼與規范中的錯誤相匹配(對於錯誤消息(例如消息過大或無效的UTF-8))。對於連接過程中的其他意外錯誤,將使用非1000 Normal Closure
狀態代碼。
jmeter配置
1.negotitate請求
2.打開websocket連接
3.發送webscoket數據
在SignalR中,總是要從客戶端發送的第一幀是HandshakeRequest {“ protocol”:“ json”,“ version”:1}
4.添加websocket心跳機制
SignalR的心跳機制是底層自動發送的,而且客戶端和服務端並不會構成實時響應,就類似於,服務端和客戶端兩者分別自己發送心跳,我們模擬一個客戶端的發送心跳
這個使用jmeter的Sampler-->websocket Single Write Sampler 添加即可,保持與websocket連接,發送內容為 {"type":6}
5.其他
- 基於SignalR的websocket在模擬測試發送消息的時候,都需要在后面添加一個特殊符號,我們可以從web頁面F12中【NetWork】里抓取這個特殊符號。
- jmeter中的websocket組件似乎針對廣播模式的場景支持不太友好,因為廣播模式下,客戶端不知道什么時候會收到服務端的消息,或者說會一直收到服務端的消息(並發人數夠多的時候),而如果此時沒有讀取websocket中的(通道中)的消息,可能會造成網路堵塞,然后測試用例就開始出現異常,從而出現連接斷開
不使用 SignalR 的 WebSocket
微軟官方也提供了不使用 SignalR 的 WebSocket的方法,有興趣的小伙伴可以研究下:
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0
其他參考