我有弹簧启动应用程序使用websocket并嵌入ActiveMQ,当用户(TestUser)同时在两个不同的浏览器中订阅 / user / TestUser / reply 时,发送消息给他一个浏览器收到另一个没有,再发新一个第二个接收而不是第一个......等等。
向 / user / TestUser / reply 发送邮件时的预期,如果他同时打开两个浏览器,则应同时在两个浏览器中收到邮件。
的pom.xml
<!-- WebSocket libraries -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-stomp</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-kahadb-store</artifactId>
<scope>runtime</scope>
</dependency>
WebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app")
.setUserDestinationPrefix("/user")
.enableStompBrokerRelay("/user");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").addInterceptors(new HttpHandshakeInterceptor()).withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new TopicSubscriptionInterceptor());
}
@Bean
public BrokerService broker() throws Exception {
BrokerService broker = new BrokerService();
broker.setSchedulePeriodForDestinationPurge(10000);
broker.addConnector("stomp://localhost:61613");
PolicyMap policyMap = new PolicyMap();
PolicyEntry policyEntry = new PolicyEntry();
policyEntry.setGcInactiveDestinations(true);
policyEntry.setInactiveTimeoutBeforeGC(30000);
policyEntry.setQueue(">");
List<PolicyEntry> entries = new ArrayList<PolicyEntry>();
entries.add(policyEntry);
policyMap.setPolicyEntries(entries);
broker.setDestinationPolicy(policyMap);
return broker;
}
UI
function connect() {
var socket = new SockJS('/websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/user/TestUser/reply', function (greeting) {
message = greeting;
showGreeting(greeting);
}, {'ack': 'client-individual'});
});
}
答案 0 :(得分:0)
如果您更改UI代码以使用主题,请执行以下操作:
stompClient.subscribe('/topic/reply/'+ frame.headers['user-name'], function (greeting) { ...
并将中继配置为/ topic
config.setApplicationDestinationPrefixes("/app")
.enableStompBrokerRelay("/topic");
并发送消息,如:
simpMessageTemplate.convertAndSend("/topic/reply/" + principal, payload, headers);
比每个连接的客户端都会收到他的消息副本。
frame.headers ['user-name']一些带有当前登录主体的spring magic header,在连接时返回。不知道是谁加了它,也许是春天安全。
如果你想用匿名用户做,你必须在客户端生成用户ID,将其存储在某处(localstorage或cookie)并传递到服务器端以用作主要名称。
编辑:
如果你想只与一个用户重新连接主题,你可以这样做(在TopicSubscriptionInterceptor中):
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
switch (sha.getCommand()) {
case SUBSCRIBE:
case SEND:
if(!sha.getDestination().equals("/topic/reply/" + sha.getUser().getName()) return null;
break;
}
...
return message;
}