Spring WebSockets的documentation声明:
4.4.13。用户目的地
应用程序可以发送针对特定用户的消息,Spring的STOMP支持可识别前缀为" / user /"以此目的。例如,客户端可能订阅目的地" / user / queue / position-updates"。该目的地将由UserDestinationMessageHandler处理并转换为用户会话唯一的目的地,例如, " /队列/位置更新 - user123&#34 ;.这样可以方便地订阅一般命名的目的地,同时确保不会与订阅同一目的地的其他用户发生冲突,这样每个用户都可以接收到唯一的库存位置更新。
这应该在RabbitMQ作为代理的多服务器环境中工作吗?
据我所知,用户的队列名称是通过附加simpSessionId
生成的。使用推荐的客户端库stomp.js时,会导致第一个用户获取队列名称"/queue/position-updates-user0"
,下一个用户获取"/queue/position-updates-user1"
,依此类推。
这反过来意味着连接到不同服务器的第一批用户将订阅同一队列("/queue/position-updates-user0"
)。
我可以在文档中找到对此的唯一引用:
在多应用程序服务器方案中,用户目标可能仍未解析,因为用户已连接到其他服务器。在这种情况下,您可以配置目标以广播未解析的消息,以便其他服务器有机会尝试。这可以通过Java config中的MessageBrokerRegistry的userDestinationBroadcast属性和XML中的message-broker元素的user-destination-broadcast属性来完成。
但这只能使用户可以从与建立Web套接字的服务器不同的服务器进行通信。
我觉得我错过了什么?无论如何都要将Spring配置为能够在多服务器环境中安全地使用MessagingTemplate.convertAndSendToUser(principal.getName(), destination, payload)
吗?
答案 0 :(得分:0)
如果需要进行身份验证(我假设他们的凭据存储在数据库中),您始终可以使用他们的数据库唯一用户ID进行身份验证。
我所做的是当用户登录时,他们会自动订阅两个主题,一个account|system
主题用于系统范围的广播,account|<userId>
主题用于特定广播。
您可以尝试为每个人订阅notification|<userid>
之类的订阅,然后向该主题发送消息,他们将收到该消息。
由于用户ID对每个用户都是唯一的,因此只要每个环境都访问相同的数据库信息,您就不应该在群集环境中出现问题。
这是我的发送方法:
public static boolean send(Object msg, String topic) {
try {
String destination = topic;
String payload = toJson(msg); //jsonfiy the message
Message<byte[]> message = MessageBuilder.withPayload(payload.getBytes("UTF-8")).build();
template.send(destination, message);
return true;
} catch (Exception ex) {
logger.error(CommService.class.getName(), ex);
return false;
}
}
我的目的地已预先格式化,因此,如果我想向ID为1的用户发送消息,则目的地类似于/topic/account|1
。
我创建了一个乒乓控制器,为连接的用户测试websockets,看看他们的环境是否允许使用websockets。我不知道这是否对您有所帮助,但这在我的集群环境中有效。
/**
* Play ping pong between the client and server to see if web sockets work
* @param input the ping pong input
* @return the return data to check for connectivity
* @throws Exception exception
*/
@MessageMapping("/ping")
@SendToUser(value="/queue/pong", broadcast=false) // send only to the session that sent the request
public PingPong ping(PingPong input) throws Exception {
int receivedBytes = input.getData().length;
int pullBytes = input.getPull();
PingPong response = input;
if (pullBytes == 0) {
response.setData(new byte[0]);
} else if (pullBytes != receivedBytes) {
// create random byte array
byte[] data = randomService.nextBytes(pullBytes);
response.setData(data);
}
return response;
}