SpringCloud 分布式系统 webSocket集群解决办法

问题描述

微服务分布式多应用场景中,由于ws的session无法序列化到redis,所以集群中,无法将所有WebSocket Session都缓存到redis进行session共享。Session 都存储在各应用当中

解决办法-基于Redis 的发布订阅

redis 订阅配置类:

@Configuration
public class RedisPublishConfig {

    /**
     * redis消息监听器容器 
     * @param connectionFactory
     * @param listenerAdapter
     * @return
     */
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                            MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("testChannel"));
        return container;
    }

    /**
     * 消息监听器适配器
     * @param redisMsg
     * @return
     */
    @Bean
    MessageListenerAdapter listenerAdapter(RedisMsg redisMsg) {
        return new MessageListenerAdapter(redisMsg, "receiveMessage");
    }

接收信息接口:


@Component
public interface RedisMsg {

    /**
     * 接收信息
     * @param message
     */
    public void receiveMessage(String message);
}

webSocket配置:

@Slf4j
@ServerEndpoint(value = "/wss/testSocket")
@Component
public class TestSocket implements RedisMsg {

    // 这里使用静态,让 service 属于类
    private static RedisTemplate redisTemplate;

    // 注入的时候,给类的 service 注入
    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        OrderingOnlineSocket.redisTemplate = redisTemplate;
    }


    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
     */
    private static Map<String,Session> socketMap = new ConcurrentHashMap<>();


    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        log.info("id:{} socket 连接建立 ---->", session.getId());
        socketMap.forEach((k,v)->{
            sendMessageToUser(k, "msg");
        });
        // 存放 session
        socketMap.put(”key“, session);
    }
    
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        if (session.isOpen()) {
            try {
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        socketMap.remove();
        log.info("socket 关闭");
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("来自客户端的消息------>{}", message);
        //广播消息到各个订阅者
        redisTemplate.convertAndSend("testChannel", message);
    }

    /**
     * 发送信息给指定用户
     * @param clientId
     * @param message
     * @return
     */
    public boolean sendMessageToUser(String clientId, String message) {
        Session session = socketMap.get(clientId);
        if(session==null) {
            return false;
        }
        if (!session.isOpen()) {
            return false;
        }
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.info("发送消息失败----->{}",e.getMessage());
        }
        return true;
    }


    /**
     * 发生错误时调用
     * */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误----->{}",error);
    }

	/**
     * 广播接收信息
     * @param message
     */
    @Override
    public void receiveMessage(String message) {
        socketMap.forEach((k,v)->{
        	sendMessageToUser(k, ”msg“);
        
        });
    }

基本配置如上,具体业务调用如:


 	@Autowired
 	RedisTemplate redisTemplate

	@Override
	public void test(String cno) {
			//	 业务逻辑
			//广播消息到各个订阅者 testChannel 
			redisTemplate.convertAndSend("testChannel", "msg");
	}

以上就是分布式系统使用webSocket 基于Redis 发布订阅解决方案

你可能感兴趣的:(SpringCloud,WebScoket,分布式系统)