引言
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的中间件,它支持的通讯模式有WebSocketsSSELong 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
其他参考