Spring Boot 3响应式WebSocketHandler中onUncaughtException处理问题
问题解决方案
根因分析
你当前代码无法捕获异常的核心原因是:
- Spring WebFlux的
WebSocketHandler处理逻辑需要同时覆盖**入站(客户端到服务端)和出站(服务端到客户端)**两个数据流,你现有代码只返回了出站send操作的Mono,完全没有订阅入站的receive流。 - 你遇到的
Connection reset是TCP层入站事件触发的异常,只会传导到入站流中,自然不会触发你写在出站流上的doOnError逻辑。 - 另外如果你的
myFluxSource是全局共享流,没有做多订阅隔离的话,单客户端错误也可能影响全局流的可用性。
修复方案
@Component("myWebSocketHandler") public class MyWebSocketHandler implements WebSocketHandler { // 存储活跃会话的全局集合,用于服务端统一推送消息,按需调整实现 private final Set<WebSocketSession> activeSessions = ConcurrentHashMap.newKeySet(); // 全局消息源提前做热流转换,支持多客户端独立订阅,全局异常兜底避免上游错误终止所有推送 private final Flux<?> myFluxSource = yourOriginalMessageFlux .publish() .autoConnect() .onErrorResume(e -> { System.err.println("全局消息源异常:" + e.getMessage()); return Flux.never(); }); @Override public Mono<Void> handle(WebSocketSession session) { // 新连接建立时加入活跃会话集合 activeSessions.add(session); // 订阅入站流,捕获客户端侧触发的连接异常、断连事件 Mono<Void> receiveMono = session.receive() .doOnNext(msg -> { // 如需处理客户端发来的消息在此处编写逻辑,不需要可留空 }) .then(); // 出站流,负责给当前客户端推送消息 Mono<Void> sendMono = session.send( myFluxSource .map(p -> session.textMessage(p.toString())) .doOnError(e -> System.err.println("会话"+session.getId()+"出站异常:"+e.getMessage())) .onErrorResume(e -> Mono.empty()) ); // 合并入站、出站流,任意一边终止/报错都会触发整个处理链路结束 return Mono.when(receiveMono, sendMono) .doOnError(e -> System.err.println("会话"+session.getId()+"全局异常:"+e.getMessage())) // 所有场景下都清理会话:正常断开、异常中断、主动取消订阅 .doFinally(signalType -> { activeSessions.remove(session); System.out.println("会话"+session.getId()+"已清理,当前活跃会话数:"+activeSessions.size()); }) // 异常兜底,避免错误传导到上层Netty容器 .onErrorResume(e -> Mono.empty()); } }
关键点说明
- 异常全捕获:入站流和出站流合并后,不管是TCP连接重置、客户端主动断连还是服务端推送异常,都会被整个处理链的异常逻辑捕获,不会再出现漏处理的Netty未捕获异常日志。
- 多客户端隔离:每个会话的处理链路完全独立,单个会话的错误只会触发当前会话的清理逻辑,不会影响其他正常会话。全局消息源做了热流隔离,单个订阅的错误不会终止上游消息生产,其他客户端的推送不受影响。
- 自动清理故障会话:
doFinally会在会话所有终止场景触发,保证故障会话及时从活跃集合中移除,不会出现无效会话堆积、推送消息到已断开客户端的问题。
内容的提问来源于stack exchange,提问作者lafual
相关产品推荐
相关产品推荐

