Spring Boot和WebSockets-使用Stomp协议通过安全的WebSocket发送高吞吐量时失去连接

时间:2019-06-27 08:53:13

标签: spring-boot spring-security websocket stomp sockjs

我有2个WebSockets端点:

registry.addEndpoint("/ws-handshake").withSockJS();
registry.addEndpoint("/api/admin/ws-handshake").withSockJS();

第一个未经身份验证,第二个受Spring Boot Security保护,使用与HTTP安全相同的配置,并使用OAuth2。授权标头用于连接到安全端点。

我使用一个简单的for循环,因为这是一个POC,但是生产中将使用相同数量的数据来返回很多记录,这是通过使用Spring Boot提供的SimpMessageSendingOperations类完成的:

    private SimpMessageSendingOperations messagingTemplate;

    @MessageMapping("/tail-topic")
    public void tailLogSendToTopic(@Payload WebSocketPoCPayload payload) throws InterruptedException {
        String topicName = "/topic/logentries";
        for (int i = 0; i < 3000; i++) {
            WebSocketPoCMessage message = new WebSocketPoCMessage(String.format("%d : returning payload %s", i, payload.getName()));
            messageTemplate.convertAndSend(topicName, message);
            // TimeUnit.MILLISECONDS.sleep(50);
        }
        log.info("done returning all messages");
    }

当我通过不安全的端点发送请求时,一切运行正常,我在客户端收到了一切。

当我通过安全端点发送请求时,一切正常,直到某个有效负载编号(例如1499,始终是相同的有效负载编号)为止。 除非我取消注释超时,否则需要超过10毫秒,否则我将失去连接,但以后的连接数会丢失。

如果我的安全设置看到的流量过多,似乎断开了连接,我的安全配置中是否缺少某些内容?

2 个答案:

答案 0 :(得分:1)

我想我已经找到了解决这个问题的真正方法, 尽管该框架是非阻塞的,但在发送每条消息后,它仍会等待rabbitMQ stomp broker 的响应。 在高吞吐量中发生的事情是有时响应丢失并且它永远挂起等待,在 StompBrokerRelayMessageHandler 中,在 future.get() 行中:

        try {
            ListenableFuture<Void> future = super.forward(message, accessor);
            if (message.getHeaders().get(SimpMessageHeaderAccessor.IGNORE_ERROR) == null) {
                future.get();
            }
            return future;
        }
        catch (Throwable ex) {
            throw new MessageDeliveryException(message, ex);
        }

对我有用的解决方案是在不等待响应的情况下发送消息,通过添加消息标头,如下所示:

    SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
    accessor.setHeader(SimpMessageHeaderAccessor.IGNORE_ERROR, true);
    accessor.setLeaveMutable(true);
    messagingTemplate.convertAndSend(destination, message, accessor.getMessageHeaders());

当然在这种情况下,一些消息可能会丢失,但对于我的用例来说已经足够了。

另一种有效的解决方案是使用超时和单线程执行程序包装 convertAndSend 行,然后重试

干杯

答案 1 :(得分:0)

由于对此似乎没有任何回应,我将接受临时解决方法作为解决方案:

private SimpMessageSendingOperations messagingTemplate;

@MessageMapping("/tail-topic")
public void tailLogSendToTopic(@Payload WebSocketPoCPayload payload) throws InterruptedException {
    String topicName = "/topic/logentries";
    for (int i = 0; i < 3000; i++) {
        WebSocketPoCMessage message = new WebSocketPoCMessage(String.format("%d : returning payload %s", i, payload.getName()));
        messageTemplate.convertAndSend(topicName, message);
        TimeUnit.MILLISECONDS.sleep(50);
    }
    log.info("done returning all messages");
}