SignalR-Client接收重复消息:服务端仅单次加组仍多次触发客户端回调
问题根因
服务端的订阅、分组逻辑无异常,问题出在客户端事件注册机制:
SignalR客户端的hubConnection.on()方法是累加注册逻辑,每次调用都会为指定事件新增一个回调实例,不会自动覆盖之前注册的同事件回调。你每次进入fixture详情页都会执行一次注册逻辑,多次进入就会注册多个完全独立的回调,服务端单次推送消息时,所有已注册的回调都会依次触发,所以表现为回调触发次数和订阅调用次数一致。
排查验证方法
- 每次进入详情页后,执行代码
console.log(this._ws.hubConnection.listeners(MatchActionWSEvent.RECEIVED_NEW_ACTIONS).length),输出值会和你进入详情页的次数完全匹配,直接证明存在多个重复回调。 - 在回调内打印随机标识,多次进入页面后触发消息推送,会看到多个不同标识的日志输出,可排除服务端重复发消息的可能。
解决方案
方案1:组件销毁时主动移除回调(推荐)
利用AngularJS的$destroy生命周期钩子,在用户离开详情页、组件销毁时,主动移除对应事件的回调,同时可按需调用服务端取消订阅接口释放组资源:
// 先将回调函数存为实例属性,方便后续移除 this.newActionsHandler = receivedData => { try { const receivedObject = JSON.parse(receivedData); this._store.dispatch(receivedNewActions(receivedObject)); } catch(err) { console.log('RECEIVED_NEW_ACTIONS error', err) } } // 注册事件回调 this._ws.hubConnection.on(MatchActionWSEvent.RECEIVED_NEW_ACTIONS, this.newActionsHandler); // 监听组件销毁事件 $scope.$on('$destroy', () => { // 移除对应事件的指定回调 this._ws.hubConnection.off(MatchActionWSEvent.RECEIVED_NEW_ACTIONS, this.newActionsHandler); // 可选:调用服务端取消订阅接口,将当前连接从fixture组移除 this._ws.hubConnection.invoke('UnsubscribeFixtureActions', 当前页面的fixtureId); });
配套的服务端可选取消订阅逻辑:
public async Task UnsubscribeFixtureActions(string fixtureId) { _broadcastService.RemoveClientSubscribtion(Context.ConnectionId, SocketMessageType.NewFixtureAction); await Groups.RemoveFromGroupAsync(Context.ConnectionId, fixtureId); }
方案2:注册前清空已有回调(快速修复)
如果不方便处理销毁逻辑,可在注册新回调前先清空该事件下所有已注册的回调,确保每次仅保留一个生效的回调:
// 先移除该事件下所有已注册的回调 this._ws.hubConnection.off(MatchActionWSEvent.RECEIVED_NEW_ACTIONS); // 再注册新回调 this._ws.hubConnection.on(MatchActionWSEvent.RECEIVED_NEW_ACTIONS, receivedData => { try { const receivedObject = JSON.parse(receivedData); this._store.dispatch(receivedNewActions(receivedObject)); } catch(err) { console.log('RECEIVED_NEW_ACTIONS error', err) } })
内容的提问来源于stack exchange,提问作者Gordonik
相关产品推荐
相关产品推荐

