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

SimpMessagingTemplate无法向指定用户发送消息问题排查

问题描述

我想实现服务端向特定用户推送消息的功能,流程是客户端订阅指定主题,服务端向该主题发送消息。目前遇到的问题:

  • 客户端订阅了/user/{userId}/queue/friend-requests(示例/user/7/queue/friend-requests)和/topic
  • 服务端WebSocket连接已将Principal名称设为用户ID(打印userRegistry.getUsers()能看到name=[7])
  • 调用simpMessagingTemplate.convertAndSend("/topic", "topic")时,客户端能正常接收消息
  • 但调用simpMessagingTemplate.convertAndSendToUser("7", "/queue/friend-requests", "user specific")时,客户端无响应,服务端也无日志输出

相关代码如下:

服务端控制器代码

@Controller
public class FriendRequestController {
    private SessionToken sessionToken;
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;
    @Autowired
    private SimpUserRegistry userRegistry;
    @Autowired
    private FriendRequestService friendRequestService;

    @MessageMapping("/friend-requests")
    public void sendFriendRequest(@Payload FriendRequest friendRequest, Principal principal) throws InvalidSessionTokenException{

        System.out.println(userRegistry.getUsers());
        System.out.println(principal.getName());

        System.out.println("- - - - - - - - - -");

        simpMessagingTemplate.convertAndSendToUser("7", "/queue/friend-requests", "user specific");
        simpMessagingTemplate.convertAndSend("/topic", "topic");

        System.out.println("- - - - - - - - - -");
    }
}

服务端配置类代码

@Configuration
@EnableWebSocketMessageBroker
@ComponentScan(basePackages = {"com/swoosh/springbootchatapp/chatapp/friend/system/stomp/friendrequest/controller"})
public class StompConfig implements WebSocketMessageBrokerConfigurer
{

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry){
        registry.addEndpoint("/friend-requests").setAllowedOrigins("*").setHandshakeHandler(new StompHandshakeHandler());
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry){
        registry.enableSimpleBroker("/queue", "/topic", "/user");
        registry.setApplicationDestinationPrefixes("/app");
        registry.setUserDestinationPrefix("/user");
    }
}

客户端订阅代码

String destination = "/user/" + Main.getApplicationState().getUserId() + "/queue/friend-requests";
stompFriendRequestsHandler.getSession().subscribe(destination, new StompFrameHandler() {
    @Override
    public Type getPayloadType(StompHeaders headers) {
        return String.class;
    }

    @Override
    public void handleFrame(StompHeaders headers, Object payload) {
        System.out.println("- - - - - - - - - - - - - - - - - - -");
        System.out.println("recived the user specifiq");
        System.out.println(payload);
    }
});

stompFriendRequestsHandler.getSession().subscribe("/topic", new StompFrameHandler() {
    @Override
    public Type getPayloadType(StompHeaders headers) {
        return String.class;
    }

    @Override
    public void handleFrame(StompHeaders headers, Object payload) {
        System.out.println("- - - - - - - - - - - - - - - - - - -");
        System.out.println("recieved the topic");
        System.out.println(payload);
    }
});

同时想确认:使用convertAndSendToUser时,第一个参数是否用于匹配WebSocket会话的name值?


问题分析与解决方案

1. 核心问题原因

存在两个关键冲突点导致消息无法送达:

  • 用户目标前缀与代理配置重复:configureMessageBroker中同时将/user加入了简单代理路径和用户目标前缀,Spring STOMP会自动处理/user前缀的目标路径,重复配置会导致路径解析逻辑混乱。
  • 客户端订阅路径错误:使用convertAndSendToUser时,客户端无需手动拼接/user/{userId}前缀,框架会根据用户Principal自动生成专属目标路径,手动拼接的路径与服务端发送的实际路径不匹配。

2. 具体修复步骤

步骤1:修正服务端配置

移除enableSimpleBroker中的/user前缀,只保留/queue和/topic:

@Override
public void configureMessageBroker(MessageBrokerRegistry registry){
    registry.enableSimpleBroker("/queue", "/topic"); // 移除/user
    registry.setApplicationDestinationPrefixes("/app");
    registry.setUserDestinationPrefix("/user");
}

步骤2:修正客户端订阅路径

客户端直接订阅/queue/friend-requests即可,框架会自动添加/user/{userId}前缀:

// 无需手动拼接/user/{userId}
String destination = "/queue/friend-requests";
stompFriendRequestsHandler.getSession().subscribe(destination, new StompFrameHandler() {
    @Override
    public Type getPayloadType(StompHeaders headers) {
        return String.class;
    }

    @Override
    public void handleFrame(StompHeaders headers, Object payload) {
        System.out.println("- - - - - - - - - - - - - - - - - - -");
        System.out.println("recived the user specifiq");
        System.out.println(payload);
    }
});

步骤3:确认服务端调用正确

当前simpMessagingTemplate.convertAndSendToUser("7", "/queue/friend-requests", "user specific")的调用逻辑是正确的,无需修改。

3. 参数匹配疑问解答

是的,convertAndSendToUser的第一个参数就是用来匹配WebSocket会话中Principal的name值。Spring STOMP会根据该参数找到对应用户的会话,将消息发送到该用户的专属目标路径。


内容的提问来源于stack exchange,提问作者Ponta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:08:10