带ActiveMQ的Websocket支持多会话?

时间:2018-04-12 07:44:51

标签: spring-boot activemq stomp spring-websocket

我有弹簧启动应用程序使用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'});
});
}

1 个答案:

答案 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;
}