jmeter测试基于SignalR的Websocket过程中的一些总结

时间:2024-02-01 13:40:33

引言

ASP.NET Core SignalR 作为微软官方出品的一个实时Web中间件功能库,可以使服务器端代码可以将内容立即推送到客户端。

SignalR supports the following techniques for handling real-time communication (in order of graceful fallback):

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

其他参考