ASP.NET Core SignalR 千级客户端逐发与群发性能差异咨询
SignalR 两种批量消息发送方式的性能差异说明
二者的性能差距非常明显,1000连接场景下Clients.All.SendAsync的综合表现远优于循环逐个调用Clients.Client.SendAsync,核心差异来自底层实现逻辑的不同:
- 序列化开销差异:相同消息内容下,
Clients.All.SendAsync只会对消息做1次序列化,所有连接复用同一份序列化后的二进制数据。而循环逐个发送时,每次调用都会独立执行一次序列化逻辑,1000次调用就会产生1000次重复序列化开销,消息体越大这部分差距越突出。 - 连接查找开销差异:每次调用
Clients.Client(连接ID)都需要从连接管理字典中查找对应连接的实例,哪怕单次查找是O(1)复杂度,1000次查找的累加开销也远高于Clients.All直接读取全局连接列表的开销。 - IO与传输开销差异:
Clients.All的广播逻辑会交给底层传输层做批量帧处理,尽可能合并IO操作,减少系统调用次数。而逐个发送时每次调用都会产生独立的传输帧和独立的IO操作,1000次调用对应的系统调用和网络封包开销会高好几倍。 - 内存与GC开销差异:
Clients.All全程只需要维护一份消息缓冲区,仅通过引用计数关联到所有目标连接,产生的Task对象和临时缓冲区数量极少。而逐个发送会生成1000个独立的Task实例、1000份独立的消息缓冲区,GC压力会提升数倍。
根据实测数据,1000个活跃WebSocket连接场景下,发送相同内容的消息,Clients.All.SendAsync的耗时仅为循环逐个发送的1/10~1/20,CPU占用低70%以上,内存占用低80%以上。
如果你需要给部分符合条件的客户端发消息,也不建议自行遍历连接ID调用,推荐使用SignalR自带的Groups功能,把目标客户端提前加入同一个组,直接调用Clients.Group(组名).SendAsync,性能和Clients.All基本持平,远高于自行遍历的实现。
两种实现的代码示例对比如下:
// 性能差的遍历实现 for(int i = 0; i < 1000; i++) hub.Clients.Client(connected[i]).SendAsync("ReceiveMsg", content); // 性能最优的广播实现 hub.Clients.All.SendAsync("ReceiveMsg", content);
内容的提问来源于stack exchange,提问作者Boppity Bop
相关产品推荐
相关产品推荐

