您的需求可以分为2个子任务:
维护多个节点之间的会话信息:您可以尝试由Redis支持的Spring Sessions集群(请参阅:HttpSession with Redis)。这非常简单,并且已经支持Spring Websocket(请参阅:Spring Session&WebSockets)。
处理多个tomcat实例上的websocket通信:有几种方法可以做到这一点。
- 第一种方法:使用功能齐全的代理(例如:ActiveMQ)并尝试新功能支持多个WebSocket服务器(来自:4.2.0 RC1)
- 第二种方法:使用功能齐全的代理并实现分布式
UserSessionRegistry
(例如:使用Redis:D)。DefaultUserSessionRegistry
使用内存存储的默认实现。
更新:我使用Redis编写了一个简单的实现,如果您有兴趣,请尝试
要配置功能齐全的代理(代理中继),您可以尝试:
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { ... @Autowired private RedisConnectionFactory redisConnectionFactory; @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableStompBrokerRelay("/topic", "/queue") .setRelayHost("localhost") // broker host .setRelayPort(61613) // broker port ; config.setApplicationDestinationPrefixes("/app"); } @Bean public UserSessionRegistry userSessionRegistry() { return new RedisUserSessionRegistry(redisConnectionFactory); } ...}和
import java.util.Set;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.BoundHashOperations;import org.springframework.data.redis.core.BoundSetOperations;import org.springframework.data.redis.core.RedisOperations;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;import org.springframework.messaging.simp.user.UserSessionRegistry;import org.springframework.util.Assert;public class RedisUserSessionRegistry implements UserSessionRegistry { static final String BOUNDED_HASH_KEY_PREFIX = "spring:websockets:users:"; private final RedisOperations<String, String> sessionRedisOperations; @SuppressWarnings("unchecked") public RedisUserSessionRegistry(RedisConnectionFactory redisConnectionFactory) { this(createDefaultTemplate(redisConnectionFactory)); } public RedisUserSessionRegistry(RedisOperations<String, String> sessionRedisOperations) { Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null"); this.sessionRedisOperations = sessionRedisOperations; } @Override public Set<String> getSessionIds(String user) { Set<String> entries = getSessionBoundHashOperations(user).members(); return (entries != null) ? entries : Collections.<String>emptySet(); } @Override public void registerSessionId(String user, String sessionId) { getSessionBoundHashOperations(user).add(sessionId); } @Override public void unregisterSessionId(String user, String sessionId) { getSessionBoundHashOperations(user).remove(sessionId); } private BoundSetOperations<String, String> getSessionBoundHashOperations(String username) { String key = getKey(username); return this.sessionRedisOperations.boundSetOps(key); } static String getKey(String username) { return BOUNDED_HASH_KEY_PREFIX + username; } @SuppressWarnings("rawtypes") private static RedisTemplate createDefaultTemplate(RedisConnectionFactory connectionFactory) { Assert.notNull(connectionFactory, "connectionFactory cannot be null"); StringRedisTemplate template = new StringRedisTemplate(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template; }}


