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
相关产品推荐
相关产品推荐

