重新加载页面时 Websocket 中断
Websocket breaks when the page is reloaded
我们有一个 Java 应用程序,它使用 SpringBoot 消息支持通过 websocket 上的 stomp 连接发送消息。一旦他们连接并订阅了主题,数据应该发送给特定用户,但是当我们重新加载页面时,websocket 中断并且再也不会发送任何消息。
我们在这里监听 SessionSubscribeEvent(因此我们可以在订阅后发送初始消息):
@Component
@AllArgsConstructor
public class TransactionSubscriptionListener implements ApplicationListener<SessionSubscribeEvent> {
private static final String DESTINATION_HEADER = "simpDestination";
private final RegionTransactionSender regionTransactionSender;
@Override
public void onApplicationEvent(SessionSubscribeEvent subscribeEvent) {
Object simpDestination = subscribeEvent.getMessage().getHeaders().get(DESTINATION_HEADER);
if (simpDestination == null) {
return;
}
String destination = String.valueOf(simpDestination);
if (destination.matches(RegionTransactionSender.REGEXP)) {
regionTransactionSender.send();
}
}
}
区域交易发送方实施:
@Component
@AllArgsConstructor
public class RegionTransactionSender {
public static final String REGEXP =
ApiVersionConstants.TRANSACTIONS_FOR_REGION_DESTINATION_WITH_SUBSCRIBER + "/\S*";
private static final String TOPIC_URL_PREFIX = ApiVersionConstants.TRANSACTIONS_FOR_REGION_DESTINATION + "/";
private final SimpMessageSendingOperations sendingOperations;
private final TransactionService transactionService;
private final SimpUserRegistry simpUserRegistry;
public void send() {
Set<SimpUser> users = simpUserRegistry.getUsers();
users.stream()
.filter(SimpUser::hasSessions)
.forEach(this::sendToSubscriptions);
}
private void sendToSubscriptions(SimpUser user) {
user.getSessions().forEach(session -> session.getSubscriptions()
.forEach(subscription -> sendToTopics(user, subscription)));
}
private void sendToTopics(final SimpUser user, final SimpSubscription subscription) {
String destination = subscription.getDestination();
if (destination.matches(REGEXP)) {
Optional<String> regionOptional = WebsocketUtils.retrieveOrganizationRegionFromDestination(destination);
regionOptional.ifPresent(region -> sendForRegionTopic(user, region));
}
}
private void sendForRegionTopic(final SimpUser user, final String region) {
Set<TransactionResponse> transactionsForRegion = transactionService
.getTransactionsForRegion(AbstractWebsocketSender.TRANSACTIONS_COUNT, region);
sendingOperations.convertAndSendToUser(user.getName(), TOPIC_URL_PREFIX + region, transactionsForRegion);
}
}
稍后调用了 send() 方法,但没有发送消息。
Messages visible in Chrome's network debugging tool
如您所见,我们的其他网络套接字 (systemBalanceSummary) 运行良好。不同之处在于,在 systemBalanceSummary 上,我们将消息发送到非用户特定的目的地。
还值得一提的是,当我们第一次访问该网站时,一切正常。
为什么当我们重新加载页面时 websocket 中断?
编辑
经过一些调试,我们发现即使触发了订阅事件,SimpUserRegistry 中也没有用户,但我们不知道是什么原因造成的。
我已经找到了解决方案。
首先,您需要实现 SimpUserRegistry
而不是使用 DefaultSimpUserRegistry
。原因是 DefaultSimpUserRegistry 似乎在 SessionConnctedEvent
被触发后添加用户并且它并不总是连接。我更改了它,以便在 SessionConnectEvent
之后添加用户。
这解决了重新加载后用户注册表中没有用户的问题。如果这不是问题,您可以跳过它。
之后我更改了 convertAndSendToUser
方法的用法。在问题数据中提供的代码被发送到用户名。我改变了它,所以我将数据发送到 sessionId,但也添加了一些 headers。这是相关代码:
private void sendForRegionTopic(final String region, final String sessionId) {
Set<TransactionResponse> transactionsForRegion = transactionService
.getTransactionsForRegion(AbstractWebsocketSender.TRANSACTIONS_COUNT, region);
sendingOperations.convertAndSendToUser(sessionId,
TOPIC_URL_PREFIX + region,
transactionsForRegion,
createHeaders(sessionId));
}
private MessageHeaders createHeaders(final String sessionId) {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
accessor.setSessionId(sessionId);
accessor.setLeaveMutable(true);
return accessor.getMessageHeaders();
}
我们有一个 Java 应用程序,它使用 SpringBoot 消息支持通过 websocket 上的 stomp 连接发送消息。一旦他们连接并订阅了主题,数据应该发送给特定用户,但是当我们重新加载页面时,websocket 中断并且再也不会发送任何消息。
我们在这里监听 SessionSubscribeEvent(因此我们可以在订阅后发送初始消息):
@Component
@AllArgsConstructor
public class TransactionSubscriptionListener implements ApplicationListener<SessionSubscribeEvent> {
private static final String DESTINATION_HEADER = "simpDestination";
private final RegionTransactionSender regionTransactionSender;
@Override
public void onApplicationEvent(SessionSubscribeEvent subscribeEvent) {
Object simpDestination = subscribeEvent.getMessage().getHeaders().get(DESTINATION_HEADER);
if (simpDestination == null) {
return;
}
String destination = String.valueOf(simpDestination);
if (destination.matches(RegionTransactionSender.REGEXP)) {
regionTransactionSender.send();
}
}
}
区域交易发送方实施:
@Component
@AllArgsConstructor
public class RegionTransactionSender {
public static final String REGEXP =
ApiVersionConstants.TRANSACTIONS_FOR_REGION_DESTINATION_WITH_SUBSCRIBER + "/\S*";
private static final String TOPIC_URL_PREFIX = ApiVersionConstants.TRANSACTIONS_FOR_REGION_DESTINATION + "/";
private final SimpMessageSendingOperations sendingOperations;
private final TransactionService transactionService;
private final SimpUserRegistry simpUserRegistry;
public void send() {
Set<SimpUser> users = simpUserRegistry.getUsers();
users.stream()
.filter(SimpUser::hasSessions)
.forEach(this::sendToSubscriptions);
}
private void sendToSubscriptions(SimpUser user) {
user.getSessions().forEach(session -> session.getSubscriptions()
.forEach(subscription -> sendToTopics(user, subscription)));
}
private void sendToTopics(final SimpUser user, final SimpSubscription subscription) {
String destination = subscription.getDestination();
if (destination.matches(REGEXP)) {
Optional<String> regionOptional = WebsocketUtils.retrieveOrganizationRegionFromDestination(destination);
regionOptional.ifPresent(region -> sendForRegionTopic(user, region));
}
}
private void sendForRegionTopic(final SimpUser user, final String region) {
Set<TransactionResponse> transactionsForRegion = transactionService
.getTransactionsForRegion(AbstractWebsocketSender.TRANSACTIONS_COUNT, region);
sendingOperations.convertAndSendToUser(user.getName(), TOPIC_URL_PREFIX + region, transactionsForRegion);
}
}
稍后调用了 send() 方法,但没有发送消息。
Messages visible in Chrome's network debugging tool
如您所见,我们的其他网络套接字 (systemBalanceSummary) 运行良好。不同之处在于,在 systemBalanceSummary 上,我们将消息发送到非用户特定的目的地。 还值得一提的是,当我们第一次访问该网站时,一切正常。
为什么当我们重新加载页面时 websocket 中断?
编辑
经过一些调试,我们发现即使触发了订阅事件,SimpUserRegistry 中也没有用户,但我们不知道是什么原因造成的。
我已经找到了解决方案。
首先,您需要实现 SimpUserRegistry
而不是使用 DefaultSimpUserRegistry
。原因是 DefaultSimpUserRegistry 似乎在 SessionConnctedEvent
被触发后添加用户并且它并不总是连接。我更改了它,以便在 SessionConnectEvent
之后添加用户。
这解决了重新加载后用户注册表中没有用户的问题。如果这不是问题,您可以跳过它。
之后我更改了 convertAndSendToUser
方法的用法。在问题数据中提供的代码被发送到用户名。我改变了它,所以我将数据发送到 sessionId,但也添加了一些 headers。这是相关代码:
private void sendForRegionTopic(final String region, final String sessionId) {
Set<TransactionResponse> transactionsForRegion = transactionService
.getTransactionsForRegion(AbstractWebsocketSender.TRANSACTIONS_COUNT, region);
sendingOperations.convertAndSendToUser(sessionId,
TOPIC_URL_PREFIX + region,
transactionsForRegion,
createHeaders(sessionId));
}
private MessageHeaders createHeaders(final String sessionId) {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
accessor.setSessionId(sessionId);
accessor.setLeaveMutable(true);
return accessor.getMessageHeaders();
}