关于spring框架使用websocket + stomp协议时的发消息顺序问题

还是继续上一篇stomp的问题,这次是在实际使用中又有了新发现,那就是java后端向前端发送stomp消息时,存在底层异步操作造成的消息顺序不确定性问题,如下:

    @Autowired
    SimpMessageSendingOperations messagingTemplate;

    @MessageMapping("/create")
    @SendToUser("/queue/reply")
    public ReplyDTO create(@Payload CreateDTO msg, SimpMessageHeaderAccessor accessor) throws Exception {
        String userId = getUserId(accessor);
        if (userId.isEmpty())
            return buildNotLoggedInReply(accessor);
        int createId = myService.create(userId, msg.getTypeName());
        CreateInfo ci = myService.info(createId);
        String infoStr = new ObjectMapper().writeValueAsString(ci);

        messagingTemplate.convertAndSendToUser(
                Objects.requireNonNull(accessor.getSessionId()),
                "/queue/response",
                buildOkResp(accessor),
                createHeaders(accessor.getSessionId())
        );

        return buildReply(accessor, infoStr);
    }

上面是一段很简单的stomp收发消息代码,但是在create方法内部除了常规return返回要发送消息DTO实例外,还通过SimpMessageSendingOperations单独给同一个session发送了另一个消息response,按目前看到的这部分代码我们认为,/queue/response应该先于/queue/reply消息到达前端,因为这也没有使用多线程或其他会导致顺序错乱的逻辑,而实际程序运行情况却并非如此,在比较极端的情况下,如程序跑在资源非常受限的虚拟机上时,会发现偶尔会有reply先于response的情况!

通过现象判断,spring stomp框架底层应该是有某种多线程处理上层待发送消息的机制,根据这个想法进行了一番搜索,果然在github上发现了spring项目的这个issue:https://github.com/spring-projects/spring-framework/issues/18562 Preserve order of broker messages [SPR-13989],看提问人描述的现象跟我现在遇到的问题很像,官方解释就是底层存在异步操作(有一点刚才没有明确,上面的代码里,response发送的DTO内容相对复杂,而后面的reply则相对简单),在性能不足时,就会导致调用publish的顺序与对端接收到的消息顺序不一致的问题,这点在新版本spring中给出了一种解决方案:“preservePublishOrder”,具体可以参考:https://docs.spring.io/spring-framework/reference/web/websocket/stomp/ordered-messages.html,按照官方说法,加上这个参数可以强制底层保证publish消息顺序,但会有一些性能损失。

按照调查发现,找了下,结果发现我这用的spring版本比较旧,还没加上这个preservePublishOrder,而暂时又不想升级,更不想损失一点性能,所以换了一种思路,将需要保证顺序且连续发送的消息合并为一个大消息,一次发送,这样就回避了这个顺序问题。

博主友情提示:

如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。