spring-boot - 跨测功机向用户发送 websocket 消息
问题描述
我有一个在 heroku 上运行的 spring boot 应用程序。我利用 websockets 为特定用户向客户端和服务器发送消息和从客户端和服务器发送消息。我使用 spring boot 的 SimpMessagingTemplate.convertAndSendToUser 来发送和接收消息,当用户需要从服务器返回消息时,它可以正常工作。我使用 Heroku 会话亲和性,这意味着即使我扩大会话数量,用户和 websocket 仍然共享同一个会话。
当我需要一个用户向另一个用户发送消息时,我的问题就出现了。如果两个用户都共享会话,它可以正常工作,但如果消息无法通过,则不会。
是否可以使用 SimpMessagingTemple 在不同的会话中从一个用户向另一个用户发送消息?或者我需要使用消息代理,例如 Redis。
我正在研究使用 StringRedisTemplate 实现发送消息,但不确定如何向特定用户发送消息。
private SimpMessagingTemplate messagingTemplate;
@Autowired
public MessageController(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
@MessageMapping("/secured/user-in")
public void sendToDevice(Message msg, @AuthenticationPrincipal User principal) throws Exception {
if (msg.getTo() != null) {
String email = msg.getTo();
Message out = new Message();
out.setMsg(msg.getMsg());
out.setFrom(msg.getFrom());
out.setTo(msg.getTo());
out.setSentTime(new Date());
out.setStatus(msg.getStatus());
messagingTemplate.convertAndSendToUser(email, "/secured/topic", out);
}
}
JS
function connect() {
var socket = new SockJS('/secured/user-in');
ST.stompClient = Stomp.over(socket);
var headers = {};
headers[ST.getHeader()] = ST.getToken();
ST.getStompClient().connect(headers, function (frame) {
retries = 1;
console.log('Connected: ' + frame);
ST.getStompClient().subscribe('/user/secured/topic', function (event){
var msg = JSON.parse(event.body);
showMessage(msg.msg);
});
});
}
更新 1
我猜我可以做这样的事情,就像这里所做的那样:
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor
.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
messagingTemplate.convertAndSendToUser(sessionId,"/queue/something", payload,
headerAccessor.getMessageHeaders());
但是我怎么能得到另一个用户的会话 ID,我使用 Redis 来存储会话信息:@EnableRedisHttpSession
解决方案
我的术语有点混乱,我试图在另一个测功机而不是会话上向另一个用户发送消息。
最终使用了 redis sub/pub。
因此,当控制器接收到消息时,它会发布到 redis,并且 redis MessageListenerAdapter 会调用 convertAndSendToUser 方法。
@MessageMapping("/secured/user-in")
public void sendToDevice(Message msg, @AuthenticationPrincipal User principal) throws Exception {
publishMessageToRedis(msg);
}
private void publishMessageToRedis(Message message) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String messageString = objectMapper.writeValueAsString(message);
stringRedisTemplate.convertAndSend("message", messageString);
}
redis 配置
@Bean
RedisMessageListenerContainer container( MessageListenerAdapter chatMessageListenerAdapter) throws URISyntaxException {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory());
container.addMessageListener(chatMessageListenerAdapter, new PatternTopic("message"));
return container;
}
@Bean("chatMessageListenerAdapter")
MessageListenerAdapter chatMessageListenerAdapter(RedisReceiver redisReceiver) {
return new MessageListenerAdapter(redisReceiver, "receiveChatMessage");
}
public class RedisReceiver {
private static final Logger LOG = LogManager.getLogger(RedisReceiver.class);
private final WebSocketMessageService webSocketMessageService;
@Autowired
public RedisReceiver(WebSocketMessageService webSocketMessageService) {
this.webSocketMessageService = webSocketMessageService;
}
// Invoked when message is publish to "chat" channel
public void receiveChatMessage(String messageStr) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Message message = objectMapper.readValue(messageStr, Message.class);
webSocketMessageService.sendChatMessage(message);
}
}
@Service
public class WebSocketMessageService {
private final SimpMessagingTemplate template;
private static final Logger LOG = LogManager.getLogger(WebSocketMessageService.class);
public WebSocketMessageService(SimpMessagingTemplate template) {
this.template = template;
}
public void sendChatMessage(Message message) {
template.convertAndSendToUser(message.getTo(), "/secured/topic", message);
}
}
解决方案基于此git 存储库
推荐阅读
- automation - PyDirectInput 鼠标移动非常敏感
- mysql - 如何使用 mysqldump 在 mysql 工作台中创建关系表的逻辑备份?
- python - 在 Docker 容器中使用 Python 编写输出
- sql-server - 使用游标将数据库声明为变量?SQL 服务器/T-SQL
- r - How to calculate all pairwise abs differences among many variables in R
- javascript - CSS Global margin not working inside @media
- android - How to create user from android and save it to firebase?
- c++ - How to change the operand type in the instruction from 32bit to 64bit in LLVM?
- python - matplotlib 中 SARIMAX 绘图的问题
- node.js - 为什么节点 js 服务器将 cookie 发送给邮递员而不是在本地主机上对应用程序做出反应