You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 13:57:29