Quarkus WebSocket服务器异步场景下MDC上下文传递问题求助
Quarkus WebSocket服务器异步场景下MDC上下文传递问题求助
兄弟,我仔细看了你的代码,问题核心在于Vert.x的事件循环线程模型和MDC的线程绑定特性不兼容——MDC默认绑定当前线程的上下文,但WebSocket的回调(比如@OnMessage)和EventBus的消息消费可能跑在不同线程里,导致上下文丢失。下面给你两种可行的解决方案,从手动传递到自动传播,按需选择:
方案一:手动传递MDC上下文(无需额外依赖)
这种方式简单直接,适合快速解决问题,核心思路是把MDC上下文存储到WebSocket Session里,发送EventBus消息时带上MDC内容:
1. 在WebSocket打开时保存MDC上下文到Session
修改@OnOpen方法,把当前MDC的上下文快照存在Session的属性中:
@OnOpen void onOpen(Session session, @PathParam("userId") String userId) { MDC.put("websocket.sessionId", session.getId()); MDC.put("user.id", userId); // 保存MDC上下文到Session属性 session.getUserProperties().put("mdcContext", MDC.getCopyOfContextMap()); log.info("New WebSocket Session opened."); websocketConnectionService.addConnection(userId, session); }
2. 在@OnMessage中恢复MDC并携带到EventBus
收到消息时先从Session取出MDC恢复,然后把MDC和消息一起封装发送:
// 先定义一个封装消息和MDC的DTO record MessageWithMdc(String content, Map<String, String> mdcContext) {} @OnMessage void onMessage(Session session, String message, @PathParam("userId") String userId) { // 从Session恢复MDC上下文 Map<String, String> mdcContext = (Map<String, String>) session.getUserProperties().get("mdcContext"); if (mdcContext != null) { MDC.setContextMap(mdcContext); } try { log.info("New message received."); // 把消息和MDC上下文一起发送到EventBus vertx.eventBus().send("websocket.message.new", new MessageWithMdc(message, mdcContext)); } finally { // 清理MDC,避免污染后续线程复用 MDC.clear(); } }
3. 在消费Service中恢复MDC
修改消费方法,接收封装后的消息并恢复MDC:
@ConsumeEvent("websocket.message.new") Uni<Void> handleWebSocketMessages(MessageWithMdc messageWithMdc) { // 恢复MDC上下文 if (messageWithMdc.mdcContext() != null) { MDC.setContextMap(messageWithMdc.mdcContext()); } try { final var userId = MDC.get("user.id"); log.info("'userId' exists in the MDC Context (userId=%s)".formatted(userId)); // 执行你的业务逻辑 return Uni.createFrom().voidItem(); } finally { MDC.clear(); } }
方案二:使用Quarkus上下文传播扩展(自动传递,更优雅)
如果不想手动处理上下文传递,可以用Quarkus官方的smallrye-context-propagation扩展,它能自动把MDC等上下文传播到异步线程、EventBus消息甚至Reactive流中。
1. 添加依赖
在pom.xml中加入上下文传播的依赖:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-context-propagation</artifactId> </dependency>
2. 调整WebSocket和EventBus的代码
有了这个扩展后,你只需要确保异步操作在Quarkus的上下文环境中执行:
- 对于
@OnMessage,可以用Uni来包装发送逻辑(Quarkus的Uni会自动传播上下文):
@OnMessage void onMessage(Session session, String message, @PathParam("userId") String userId) { // 用Uni包装,自动传播MDC上下文 Uni.createFrom().voidItem() .invoke(() -> { log.info("New message received."); vertx.eventBus().send("websocket.message.new", message); }) .subscribe().with(ignored -> {}); }
- 消费Service的代码不需要修改,扩展会自动把MDC上下文传递到
@ConsumeEvent的方法中:
@ConsumeEvent("websocket.message.new") Uni<Void> handleWebSocketMessages(String message) { final var userId = MDC.get("user.id"); // 现在能拿到正确的值了! log.info("'userId' exists in the MDC Context (userId=%s)".formatted(userId)); // 业务逻辑 return Uni.createFrom().voidItem(); }
另外,如果你想确保WebSocket的所有回调都自动传播MDC,还可以考虑使用Quarkus的Vert.x WebSocket路由(而不是JSR-356的@ServerEndpoint),它和上下文传播的兼容性更好,但需要调整你的WebSocket实现方式。
备注:内容来源于stack exchange,提问作者Jorge F. Sanchez
相关产品推荐
相关产品推荐

