利用redis发布/订阅功能解决websocket session共享问题

       由于websocket session不能序列化,所以不能存储在redis中。故在分布式系统中,可以通过把websocket 的session存储在服务器本地map,然后把消息发布到redis指定的频道上,每个服务器节点都订阅该频道,这样的话,消息一发布,每个节点都能接受到该消息,然后再从map中获取session,来完成消息推送。

两种方式都能实现redis的发布/订阅功能

第一种:spring-data-redis实现

1、spring配置

       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx  
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

   
   
       
       
       
       
       
           
                classpath*:jdbc.properties
                classpath*:redis.properties
                classpath*:ftpPoolConfig.properties
           

       

   

   
   
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
   

   
   
       
       
       
       
       
   

   
   
   
       
   

   
              class="org.springframework.data.redis.connection.RedisStandaloneConfiguration">
       
       
       
       
   

   
   
       
       
       
       
   

   
   
   
   
   
       
       
       
       
       
   

       
   
       
       
           
               
                   
                   
                       
                   

               

           

       

   

   
   
       
   

   

   
   
       
       
       
       
           
               
                   
               

           

       

       
           
               
               
           

       

   

   
   
       
       
       
       
   

   
   
       
   

   
       
           
           
           
           
           
           
           
           
           
           
           
       

   

   
       
       
   

   
   
       
       
       
       
       
       
       
       
       
       
       
       
   

   
       
   

   
   
       
   

   
   
   
   

   
   
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
   

   
   
       
   

   
   
       
   

   
   
       
   

   
   

2、消息监听

public class RedisMessageListener implements MessageListener {
    Map sessionMap = WebSocketUtils.sessionMap;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        RedisSerializer serializer = RedisUtils.redisTemplate.getValueSerializer();
        Object body = serializer.deserialize(message.getBody());  
        JSONObject jsonObject = JSONObject.parseObject(String.valueOf(body));
        String userNo = jsonObject.getString("userNo");
        if(SysConstant.WEBSOCKET_TYPE_1.equals(jsonObject.getString("type"))) {
            if(sessionMap.containsKey(userNo)) {
                WebSocketUtils.sendLoginMessage(sessionMap.get(userNo), jsonObject.getString("id"), (String.valueOf(body)));
            }
        }else {
            WebSocketUtils.sendMessage(sessionMap.get(userNo), (String.valueOf(body)));
        }
    }
}

3、发布消息

redisTemplate.convertAndSend("websocket." + userNo, jsonObject);

第二种:原生实现

1、发布者

public class RedisPublisher extends Thread{

    JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379);
    private String channel;//频道
    private String message;//发布的消息
    String lock = new String("lock"); ;//对象锁
    public RedisPublisher(String channel, String message) {
        this.channel = channel;
        this.message = message;
    }
    
    public void publish() {
        Jedis jedis = jedisPool.getResource();   //连接池中取出一个连接
        jedis.publish(channel, message);   //从 channel 的频道上推送消息
    }
    
    @Override
    public void run() {
        System.out.println("000000000000000");
        synchronized (lock) {
            Jedis jedis = jedisPool.getResource();   //连接池中取出一个连接
            Map pubsubNumSub = jedis.pubsubNumSub(channel);
            Long pubsubNumPat = jedis.pubsubNumPat();
            jedis.publish(channel, message);   //从 channel 的频道上推送消息
        }
    }
}

2、订阅者监听

public class RedisSubListener extends JedisPubSub{
    private static Logger log = LoggerFactory.getLogger(RedisSubListener.class);
    Map sessionMap = WebSocketUtils.sessionMap;
    private String userNo;
    public RedisSubListener(String userNo){
        this.userNo = userNo;
    }
    
    //收到消息会调用
    @Override
    public void onMessage(String channel, String message) {       
        //收到消息后,进行解析发往前端
        JSONObject jsonObject = JSONObject.parseObject(message);
        String userNo = jsonObject.getString("userNo");
        if(sessionMap.containsKey(userNo)) {
            WebSocketUtils.sendMessage(sessionMap.get(userNo),message);
        }
        super.unsubscribe();
        
    }
    
    //订阅了频道会调用
    @Override
    public void onSubscribe(String channel, int subscribedChannels) {    
        log.debug(String.format("subscribe redis channel success, channel %s, subscribedChannels %d",
                channel, subscribedChannels));
    }
    
    //取消订阅 会调用
    @Override
    public void onUnsubscribe(String channel, int subscribedChannels) {   
        log.debug(String.format("unsubscribe redis channel, channel %s, subscribedChannels %d",
                channel, subscribedChannels));

    }
}

3、订阅者

public class RedisSubscriber extends Thread{
    private static Logger log = LoggerFactory.getLogger(RedisSubscriber.class);
    JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379);
    private String channel;
    String lock = new String("lock"); ;//对象锁
    public RedisSubscriber(String channel) {
        this.channel = channel;
    }

    @Override
    public void run() {
        System.out.println("777777777777");
        synchronized (lock) {
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();   //取出一个连接
                RedisSubListener redisSubListener = new RedisSubListener(channel);
                jedis.subscribe(redisSubListener, channel);    //通过subscribe 的api去订阅,入参是订阅者和频道名
            } catch (Exception e) {
                log.error(String.format("subsrcibe channel error, %s", e));
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
    }
}

 

4、测试

RedisSubscriber redisSubscriber = new RedisSubscriber(userNo);
redisSubscriber.start();

RedisPublisher redisPublisher = new RedisPublisher(userNo, jo.toString());
redisPublisher.start();

 

你可能感兴趣的:(java)