ASP.NET Core SignalR Hub方法无法重复调用问题求助
ASP.NET Core SignalR 多次调用AddRow方法失效问题排查
问题场景
我在应用中使用ASP.NET Core SignalR,服务端代码如下:
public class Broadcast : Hub { public async Task AddRow(Guid tableId) { RecordView newRow = _tableService.AddRow(tableId); await Clients.Group(tableId.ToString()).SendAsync("AddRow", newRow); } }
客户端代码如下:
function addRow() { hubConnection.send("AddRow", useTables().tableId) .then(() => { console.log("AddRow Success!") }) .catch((err) => { console.log("AddRow error: ", err) }) }
首次调用AddRow方法一切正常,但后续调用时该方法不再执行。我猜测这是因为服务器默认限制单客户端一次仅能调用一个方法,且服务器一直在等待任务完成。当我设置MaximumParallelInvocationsPerClient = 2后,该方法可被调用两次,但第三次调用又失效了。我不理解为何执行完await Clients.Group(tableId.ToString()).SendAsync("AddRow", newRow);后任务仍未被判定为完成。
可能原因及解决方案
1. 依赖服务存在同步阻塞
_tableService.AddRow(tableId)如果是同步执行的耗时操作(比如未异步化的数据库IO、文件读写),会占用Hub线程池资源,导致后续调用无法被处理。即使设置了并行调用上限,线程耗尽后依然会出现阻塞。
- 改造
AddRow为异步方法,用await调用避免同步阻塞:public async Task AddRow(Guid tableId) { RecordView newRow = await _tableService.AddRowAsync(tableId); await Clients.Group(tableId.ToString()).SendAsync("AddRow", newRow); }
2. 消息序列化或组内异常连接阻塞发送操作
SendAsync理论上在消息写入输出流后就会完成,但如果RecordView无法被正确序列化,或者组内存在未清理的异常连接,会导致底层异步操作无法完成:
- 验证
RecordView的序列化兼容性:避免循环引用、确保属性有公共getter、不使用SignalR不支持的类型(比如非可序列化的自定义类型); - 检查组内连接状态,在Hub方法中加入日志输出组内活跃连接数,排查异常连接:
public async Task AddRow(Guid tableId) { var groupName = tableId.ToString(); var clientCount = Clients.Group(groupName).Clients.Count(); _logger.LogInformation("组{GroupName}内活跃连接数:{Count}", groupName, clientCount); RecordView newRow = _tableService.AddRow(tableId); await Clients.Group(groupName).SendAsync("AddRow", newRow); }
3. 客户端未正确处理调用上下文
如果客户端之前的send调用Promise未正确resolve/reject,或者连接在调用过程中意外断开重连,会导致后续调用被队列阻塞:
- 检查客户端连接状态,确保每次调用
addRow时hubConnection.state处于Connected状态; - 在客户端添加连接重连逻辑,确保重连后重新加入对应组。
4. 验证Hub方法的任务完成状态
在Hub方法中添加详细日志,定位任务卡在哪个环节:
private readonly ILogger<Broadcast> _logger; public Broadcast(ILogger<Broadcast> logger, ITableService tableService) { _logger = logger; _tableService = tableService; } public async Task AddRow(Guid tableId) { _logger.LogInformation("开始执行AddRow,tableId: {TableId}", tableId); RecordView newRow = _tableService.AddRow(tableId); _logger.LogInformation("获取新行数据完成"); await Clients.Group(tableId.ToString()).SendAsync("AddRow", newRow); _logger.LogInformation("发送消息到组完成"); }
内容的提问来源于stack exchange,提问作者Dmitry Rodin
相关产品推荐
相关产品推荐

