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

Spring STOMP消息未送达目标会话问题排查求助

问题分析与解决方案

核心问题

你遇到的问题主要来自两个方面:

  1. 消息发送时机错误:前端在WebSocket连接未完全建立时就发送加入群组的请求,导致后端无法正确记录会话ID。
  2. 订阅路径不匹配:使用convertAndSendToUser发送消息时,Spring会自动拼接目标路径为/user/{sessionId}/personal,但前端订阅的是/personal,两者不匹配导致无法接收消息。

另外,后端代码存在潜在的空指针风险:当发送消息到不存在的群组时,groupSessions.get(groupName)会返回null,引发异常。


方案一:修复原sessionId管理逻辑

1. 后端代码修改

增加群组存在性判断,避免空指针:

@Controller
public class HomeController {
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    private final Map<String, List<String>> groupSessions = new HashMap<>();

    @MessageMapping("/group/{groupName}/join")
    public void joinGroup(@DestinationVariable String groupName, SimpMessageHeaderAccessor headerAccessor) {
        // 确保会话ID不为空再添加
        String sessionId = headerAccessor.getSessionId();
        if (sessionId != null) {
            groupSessions.computeIfAbsent(groupName, k -> new ArrayList<>()).add(sessionId);
        }
    }

    @MessageMapping("/group/{groupName}/send")
    public void sendToGroup(@DestinationVariable String groupName, @Payload String message) {
        List<String> sessions = groupSessions.get(groupName);
        if (sessions != null) {
            for (String session : sessions) {
                simpMessagingTemplate.convertAndSendToUser(session, "/personal", message);
            }
        }
    }
}

2. 前端代码修改

调整加入群组的时机,确保连接成功后再发送请求;订阅正确的用户专属路径(需获取当前会话ID):

<script>
    const socket = new SockJS('http://localhost:8080/ws-reg');
    const stompClient = Stomp.over(socket);
    let currentSessionId = null;

    function joinGroup() {
        const groupName = document.getElementById('groupInput').value;

        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: ' + frame);
            // 从SockJS连接中提取会话ID
            currentSessionId = socket._transport.url.split('/')[5];
            // 订阅当前用户的专属消息路径
            stompClient.subscribe(`/user/${currentSessionId}/personal`, function(greeting){
                console.log(JSON.parse(greeting.body));
            });
            // 连接成功后再发送加入群组请求
            stompClient.send(`/app/group/${groupName}/join`, {}, JSON.stringify({}));
        });
    }

    function sendMessage() {
        const groupName = document.getElementById('groupInput').value;
        const messageContent = document.getElementById('messageInput').value;
        stompClient.send(`/app/group/${groupName}/send`, {}, JSON.stringify({ content: messageContent }));
    }
</script>

方案二:改用群组直接订阅(更简洁推荐)

放弃手动管理会话ID,直接利用Spring WebSocket的群组消息能力,实现更简洁的逻辑:

1. 配置类修改

添加/group到消息代理的允许前缀:

@Configuration
@EnableWebSocketMessageBroker
public class WebSockConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws-reg").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/all", "/personal", "/group");
    }
}

2. 控制器简化

无需维护会话映射,直接发送消息到群组目的地:

@Controller
public class HomeController {
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/group/{groupName}/send")
    public void sendToGroup(@DestinationVariable String groupName, @Payload String message) {
        simpMessagingTemplate.convertAndSend("/group/" + groupName, message);
    }
}

3. 前端修改

用户加入群组时直接订阅对应群组的消息路径:

<script>
    const socket = new SockJS('http://localhost:8080/ws-reg');
    const stompClient = Stomp.over(socket);

    function joinGroup() {
        const groupName = document.getElementById('groupInput').value;

        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: ' + frame);
            // 订阅目标群组的消息路径
            stompClient.subscribe(`/group/${groupName}`, function(greeting){
                console.log(JSON.parse(greeting.body));
            });
        });
    }

    function sendMessage() {
        const groupName = document.getElementById('groupInput').value;
        const messageContent = document.getElementById('messageInput').value;
        stompClient.send(`/app/group/${groupName}/send`, {}, JSON.stringify({ content: messageContent }));
    }
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:27:04