由于websocket session不能序列化,所以不能存储在redis中。故在分布式系统中,可以通过把websocket 的session存储在服务器本地map,然后把消息发布到redis指定的频道上,每个服务器节点都订阅该频道,这样的话,消息一发布,每个节点都能接受到该消息,然后再从map中获取session,来完成消息推送。
两种方式都能实现redis的发布/订阅功能
第一种:spring-data-redis实现
1、spring配置
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">
2、消息监听
public class RedisMessageListener implements MessageListener {
Map
@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
Long pubsubNumPat = jedis.pubsubNumPat();
jedis.publish(channel, message); //从 channel 的频道上推送消息
}
}
}
2、订阅者监听
public class RedisSubListener extends JedisPubSub{
private static Logger log = LoggerFactory.getLogger(RedisSubListener.class);
Map
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();