SignalR Serverless下如何处理用户关闭浏览器的会话通知问题
这个问题的核心矛盾有两点:一是客户端侧的onclose回调完全依赖浏览器JS执行环境存活,遇到用户强关浏览器、设备断电、突发断网等场景时,回调根本没有执行机会;二是Azure Function Serverless模式没有常驻服务进程维护SignalR连接状态,传统自托管SignalR的服务端连接监听逻辑无法直接复用。可落地的可靠方案组合如下:
核心逻辑依赖Azure SignalR Service原生连接事件,实现100%断连覆盖
Serverless模式下所有SignalR长连接实际由Azure SignalR Service托管,服务本身会持续检测所有连接的心跳状态,不管客户端是主动断开、强关浏览器、断网、设备重启,只要连接心跳超时(默认30秒,可根据业务需求调整超时窗口),服务就会触发disconnected事件。只需在SignalR Service的上游配置中,将连接断开事件绑定到对应的Azure Function端点,事件payload会自动携带连接ID、绑定的用户标识、自定义声明等信息,直接在这个Function中编写离会通知逻辑即可,整个流程完全不依赖客户端执行,不会出现漏报。客户端侧增加低延迟兜底,缩短主动离会的感知时长
服务端心跳检测存在固定超时窗口,用户主动关闭页面、主动退出的场景,可以通过客户端事件提前触发离会通知,注意不要用普通fetch发请求——页面卸载时浏览器会直接取消所有未完成的异步fetch请求,必须使用navigator.sendBeacon(),这个API由浏览器保证请求会在页面卸载后继续发送完成,不会被中断。参考实现:
const sendLeaveNotification = () => { const targetUrl = new URL('/api/v1/Leaving', window.location.origin); targetUrl.searchParams.set('Id', currentUserId); // 鉴权信息根据接口要求放到请求体或query参数即可 const reqPayload = new Blob( [JSON.stringify({ Authorization: `Bearer ${accessToken}` })], { type: 'application/json' } ); navigator.sendBeacon(targetUrl, reqPayload); }; // 绑定SignalR正常断开事件 connection.onclose(sendLeaveNotification); // 绑定页面即将卸载事件 window.addEventListener('beforeunload', sendLeaveNotification); // 兼容移动端页面切后台、被系统回收的场景 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') sendLeaveNotification(); });
- 原方案失效的根本原因
传统自托管SignalR模式下,服务端进程常驻内存,直接持有所有长连接的上下文,断连事件可以在服务端本地即时触发;但Serverless模式下Azure Function是事件触发的短生命周期实例,本身不保存任何连接状态,所有连接状态全托管在Azure SignalR Service上,如果不绑定服务原生的连接事件,Function根本感知不到非客户端主动触发的异常断连,纯靠客户端回调的方案天然存在漏报可能。
- 配置提示:服务端心跳检测的延迟建议配置为30-60秒,配合客户端兜底逻辑,可以做到用户主动离会秒级感知、异常离会1分钟内感知,完全覆盖所有场景。不需要额外实现客户端轮询做离会检测,这类方案资源开销大,可靠性也远低于服务原生事件。
内容的提问来源于stack exchange,提问作者Marco Jr

