Redis实战——Redis的pub/Sub(订阅与发布)在java中的实现

借鉴:https://blog.csdn.net/canot/article/details/51938955

 

1. 什么是 pub/sub

Pub/Sub 功能(means Publish, Subscribe)即发布及订阅功能。基于事件的系统中,Pub/Sub 是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者 (如客户端) 以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;发布者 (如服务器) 可将订阅者感兴趣的事件随时通知相关订阅者。熟悉设计模式的朋友应该了解这与 23 种设计模式中的观察者模式极为相似。 
同样,Redis 的 pub/sub 是一种消息通信模式,主要的目的是解除消息发布者和消息订阅者之间的耦合,Redis 作为一个 pub/sub 的 server, 在订阅者和发布者之间起到了消息路由的功能。

2.Redis pub/sub 的实现

Redis 通过 publish 和 subscribe 命令实现订阅和发布的功能。订阅者可以通过 subscribe 向 redis server 订阅自己感兴趣的消息类型。redis 将信息类型称为通道 (channel)。当发布者通过 publish 命令向 redis server 发送特定类型的信息时,订阅该消息类型的全部订阅者都会收到此消息。

客户端 1 订阅 CCTV1:

127.0.0.1:6379> subscribe CCTV1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "CCTV1"
3)(integer) 1

 

 

客户端 2 订阅 CCTV1 和 CCTV2:

127.0.0.1:6379> subscribe CCTV1 CCTV2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "CCTV1"
3)(integer) 1
1) "subscribe"
2) "CCTV2"
3)(integer) 2

 

此时这两个客户端分别监听这指定的频道。现在另一个客户端向服务器推送了关于这两个频道的信息。

127.0.0.1:6379> publish CCTV1 "cctv1 is good"
(integer) 2
// 返回 2 表示两个客户端接收了次消息。被接收到消息的客户端如下所示。
1) "message"
2) "CCTV1"
3) "cctv1 is good"
----
1) "message"
2) "CCTV1"
3) "cctv1 is good"

如上的订阅 / 发布也称订阅发布到频道 (使用 publish 与 subscribe 命令),此外还有订阅发布到模式 (使用 psubscribe 来订阅一个模式)

订阅 CCTV 的全部频道

127.0.0.1:6379> psubscribe CCTV*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "CCTV*"
3)(integer) 1

当依然先如上推送一个 CCTV1 的消息时,该客户端正常接收。

3.Pub/Sub 在 java 中的实现

导入 Redis 驱动:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

 

Redis 驱动包提供了一个抽象类:JedisPubSub…继承这个类就完成了对客户端对订阅的监听。示例代码:

/**
 * redis 发布订阅消息监听器
 * @ClassName: RedisMsgPubSubListener 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018 年 8 月 22 日 上午 10:05:35  
 *
 */
public class RedisMsgPubSubListener extends JedisPubSub {
    private Logger logger = LoggerFactory.getLogger(RedisMsgPubSubListener.class);
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> unsubscribe() {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.unsubscribe();
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> unsubscribe(String... channels) {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.unsubscribe(channels);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> subscribe(String... channels) {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.subscribe(channels);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> psubscribe(String... patterns) {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.psubscribe(patterns);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> punsubscribe() {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.punsubscribe();
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> punsubscribe(String... patterns) {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.punsubscribe(patterns);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onMessage(String channel, String message) {
    logger.info(</span>"onMessage: channel[{}], message[{}]"<span style="color: rgba(0, 0, 0, 1)">,channel, message);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onPMessage(String pattern, String channel, String message) {
    logger.info(</span>"onPMessage: pattern[{}], channel[{}], message[{}]"<span style="color: rgba(0, 0, 0, 1)">, pattern, channel, message);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onSubscribe(String channel, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> subscribedChannels) {
    logger.info(</span>"onSubscribe: channel[{}], subscribedChannels[{}]"<span style="color: rgba(0, 0, 0, 1)">, channel, subscribedChannels);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onPUnsubscribe(String pattern, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> subscribedChannels) {
    logger.info(</span>"onPUnsubscribe: pattern[{}], subscribedChannels[{}]"<span style="color: rgba(0, 0, 0, 1)">, pattern, subscribedChannels);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onPSubscribe(String pattern, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> subscribedChannels) {
    logger.info(</span>"onPSubscribe: pattern[{}], subscribedChannels[{}]"<span style="color: rgba(0, 0, 0, 1)">, pattern, subscribedChannels);
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onUnsubscribe(String channel, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> subscribedChannels) {
    logger.info(</span>"channel:{} is been subscribed:{}"<span style="color: rgba(0, 0, 0, 1)">, channel, subscribedChannels);
}

}

 

如上所示, 抽象类中存在的方法。分别表示

  • 监听到订阅模式接受到消息时的回调 (onPMessage)
  • 监听到订阅频道接受到消息时的回调 (onMessage)
  • 订阅频道时的回调 (onSubscribe)
  • 取消订阅频道时的回调 (onUnsubscribe)
  • 订阅频道模式时的回调 (onPSubscribe)
  • 取消订阅模式时的回调 (onPUnsubscribe)

运行我们刚刚编写的类:

订阅者

/**
 * 订阅者
 * @ClassName: RedisSubTest 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018 年 8 月 23 日 下午 2:59:42  
 *
 */
public class RedisSubTest {
    @Test
    public void subjava() {
        System.out.println("订阅者");
        Jedis jr = null;
        try {
            jr = new Jedis("127.0.0.1", 6379, 0);// redis 服务地址和端口号
            RedisMsgPubSubListener sp = new RedisMsgPubSubListener();
            // jr 客户端配置监听两个 channel
            jr.subscribe(sp, "news.share", "news.blog");} catch (Exception e) {e.printStackTrace();
        } finally {
            if (jr != null) {jr.disconnect();
            }
        }
    }
}

发布者

/**
 * 发布者
 * @ClassName: RedisPubTest 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018 年 8 月 23 日 下午 2:59:25  
 *
 */
public class RedisPubTest {
    @Test
    public void pubjava() {
        System.out.println("发布者");
        Jedis jr = null;
        try {
            jr = new Jedis("127.0.0.1", 6379, 0);// redis 服务地址和端口号
            // jr 客户端配置监听两个 channel
            jr.publish( "news.share", "新闻分享");
            jr.publish( "news.blog", "新闻博客");} catch (Exception e) {e.printStackTrace();
        } finally {
            if (jr != null) {jr.disconnect();
            }
        }
    }
}

 

从代码中我们不难看出,我们声明的一个 redis 链接在设置监听后就可以执行一些操作,例如发布消息,订阅消息等。。。 
当运行上述代码后会在控制台输出:

此时当在有客户端向 new.share 或者 new.blog 通道 publish 消息时,onMessage 方法即可被相应。(jedis.publish(channel, message))。

 

4.Pub/Sub 在 Spring 中的实践 
导入依赖 jar

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.0.8.RELEASE</version>
</dependency>

 核心消息监听器

/**
 * redis 发布订阅消息监听器
 * @ClassName: RedisMsgPubSubListener 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018 年 8 月 22 日 上午 10:05:35  
 *
 */
public class RedisMsgPubSubListener implements MessageListener {
    private Logger logger = LoggerFactory.getLogger(RedisMsgPubSubListener.class);
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onMessage( <span style="color: rgba(0, 0, 255, 1)">final</span> Message message, <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] pattern ) {
    RedisSerializer</span>&lt;?&gt; serializer =<span style="color: rgba(0, 0, 0, 1)"> redisTemplate.getValueSerializer();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> message.getBody()是Redis的值,需要用redis的valueSerializer反序列化</span>
    logger.info("Message receive--&gt;pattern:{},message: {},{}", <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> String(pattern),
            serializer.deserialize(message.getBody()),
            redisTemplate.getStringSerializer().deserialize(message.getChannel()));
    logger.info(message.toString());
    JSONObject json </span>=<span style="color: rgba(0, 0, 0, 1)"> JSONObject.parseObject(serializer.deserialize(message.getBody()).toString());
    String cutomerId </span>= json.getString("cutomerId"<span style="color: rgba(0, 0, 0, 1)">);
    
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">可以与WebSocket结合使用,解决分布式服务中,共享Session</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(StringUtils.isNotEmpty(cutomerId)) {
        logger.info(</span>"cutomerId: {},消息:{}"<span style="color: rgba(0, 0, 0, 1)">, cutomerId, message.toString());
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
        logger.info(</span>"cutomerId 为空,无法推送给对应的客户端,消息:{}"<span style="color: rgba(0, 0, 0, 1)">, message.toString());
    }
}

}

 

 

现在我们在获取 RedisTemplate, 并给 WEB_SOCKET:LOTTERY 这个 channel publish 数据。

/**
 * 发布者
 * @ClassName: RedisMsgPubClient 
 * @Description: TODO
 * @author OnlyMate
 * @Date 2018 年 8 月 23 日 下午 3:59:33  
 *
 */
@Controller
@RequestMapping(value="/redisMsgPubClientBySpring")
public class RedisMsgPubClient {
    private Logger logger = LoggerFactory.getLogger(RedisMsgPubClient.class);
@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span> RedisTemplate&lt;Object,Object&gt;<span style="color: rgba(0, 0, 0, 1)"> redisTemplate;

@RequestMapping
@ResponseBody
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String pubMsg(HttpServletRequest request, HttpServletResponse response) {
    String cutomerId </span>= request.getParameter("cutomerId"<span style="color: rgba(0, 0, 0, 1)">).toString();
    String msg </span>= request.getParameter("msg"<span style="color: rgba(0, 0, 0, 1)">).toString();
    logger.info(</span>"发布消息:{}", request.getParameter("msg"<span style="color: rgba(0, 0, 0, 1)">).toString());
    JSONObject json </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> JSONObject();
    json.put(</span>"cutomerId"<span style="color: rgba(0, 0, 0, 1)">, cutomerId);
    json.put(</span>"msg"<span style="color: rgba(0, 0, 0, 1)">, msg);
    redisTemplate.convertAndSend(</span>"WEB_SOCKET:LOTTERY"<span style="color: rgba(0, 0, 0, 1)">, json);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> "成功"<span style="color: rgba(0, 0, 0, 1)">;
}

}

 

最后一步 reids 的配置

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.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/mvc 
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd
                        http://www.springframework.org/schema/cache   
                        http://www.springframework.org/schema/cache/spring-cache.xsd"
    default-autowire="byName">
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">description</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>redis 相关类 Spring 托管<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">description</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 开启缓存 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">cache:annotation-driven </span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="springCacheAnnotationParser"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.annotation.SpringCacheAnnotationParser"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="annotationCacheOperationSource"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.annotation.AnnotationCacheOperationSource"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">array</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ref </span><span style="color: rgba(255, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">="springCacheAnnotationParser"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">array</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="cacheInterceptor"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.interceptor.CacheInterceptor"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="cacheOperationSources"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="annotationCacheOperationSource"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="cacheOperationSource"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="annotationCacheOperationSource"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="advice"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="cacheInterceptor"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="order"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="2147483647"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)">载入 redis 配置文件</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">context:property-placeholder </span><span style="color: rgba(255, 0, 0, 1)">location</span><span style="color: rgba(0, 0, 255, 1)">="classpath:redis.properties"</span><span style="color: rgba(255, 0, 0, 1)"> ignore-unresolvable</span><span style="color: rgba(0, 0, 255, 1)">="true"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>


<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 配置JedisConnectionFactory </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="jedisConnectionFactory"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="hostName"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.host}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="port"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.port}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="password"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pass}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="database"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.dbIndex}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="poolConfig"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="jedisPoolConfig"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> &lt;constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration" /&gt; </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="poolConfig"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="jedisPoolConfig"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 配置 JedisPoolConfig 实例 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="jedisPoolConfig"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="redis.clients.jedis.JedisPoolConfig"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 最大连接数 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="maxTotal"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.maxActive}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 最大空闲时间 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="maxIdle"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.maxIdle}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 最小空闲时间 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="minIdle"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.minIdle}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 获得链接时的最大等待毫秒数,小于0:阻塞不确定时间,默认-1 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="maxWaitMillis"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.maxWaitMillis}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="testOnBorrow"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.testOnBorrow}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 在空闲时检查有效性,默认false </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="testWhileIdle"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.testOnBorrow}"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 表示idle object evitor两次扫描之间要sleep的毫秒数 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="timeBetweenEvictionRunsMillis"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.timeBetweenEvictionRunsMillis}"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="minEvictableIdleTimeMillis"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.minEvictableIdleTimeMillis}"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 表示idle object evitor每次扫描的最多的对象数 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="numTestsPerEvictionRun"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${redis.pool.numTestsPerEvictionRun}"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 配置哨兵 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> &lt;bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration"&gt;
    &lt;property name="master"&gt;
        &lt;bean class="org.springframework.data.redis.connection.RedisNode"&gt;
            &lt;property name="name" value="mymaster" /&gt;
        &lt;/bean&gt;
    &lt;/property&gt;
    &lt;property name="sentinels"&gt;
        &lt;set&gt;
            &lt;bean class="org.springframework.data.redis.connection.RedisNode"&gt;
                &lt;constructor-arg name="host" value="10.252.2.137" /&gt;
                &lt;constructor-arg name="port" value="26391" /&gt;
            &lt;/bean&gt;
            &lt;bean class="org.springframework.data.redis.connection.RedisNode"&gt;
                &lt;constructor-arg name="host" value="10.252.2.137" /&gt;
                &lt;constructor-arg name="port" value="26392" /&gt;
            &lt;/bean&gt;
            &lt;bean class="org.springframework.data.redis.connection.RedisNode"&gt;
                &lt;constructor-arg name="host" value="10.252.2.137" /&gt;
                &lt;constructor-arg name="port" value="26393" /&gt;
            &lt;/bean&gt;
        &lt;/set&gt;
    &lt;/property&gt;
&lt;/bean&gt; </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>

<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。
    StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。 
    RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。 
    就是因为序列化策略的不同,即使是同一个key用不同的Template去序列化,结果是不同的。所以根据key去删除数据的时候就出现了删除失败的问题。
</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> redis 序列化策略 ,通常情况下key值采用String序列化策略, </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 配置RedisTemplate </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="redisTemplate"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.core.RedisTemplate"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="connectionFactory"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="jedisConnectionFactory"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="keySerializer"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.serializer.StringRedisSerializer"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">property</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> &lt;property name="valueSerializer" ref="stringRedisSerializer" /&gt; value值如果是对象,这不能用stringRedisSerializer,报类型转换错误</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> &lt;property name="valueSerializer"&gt;
        hex(十六进制)的格式
        &lt;bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /&gt;
    &lt;/property&gt; </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="valueSerializer"</span> <span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> json的格式,要注意实体属性名有没有‘_’,如user_name,有的话要加注解 ,@JsonNaming会将userName处理为user_name
               @JsonSerialize
            @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class) 
           </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">property</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="cacheManager"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.support.SimpleCacheManager"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="caches"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">set</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 这里可以配置多个redis </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean
                </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="name"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="localDefault"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span><span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 缺省本地缓存 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean
                </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="name"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="WSLocalTableCache"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span><span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 单表配置 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 本地缓存2:管理缓存失效 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="com.only.mate.utils.RedisCache"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="name"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="localTest"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span><span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 本地缓存名 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="timeout"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="10"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>  <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> seconds </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="removeTimeout"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="true"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span> <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 超时移除 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">set</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">property</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 配置redis发布订阅模式 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="redisMessageListenerContainer"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.listener.RedisMessageListenerContainer"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="connectionFactory"</span><span style="color: rgba(255, 0, 0, 1)"> ref</span><span style="color: rgba(0, 0, 255, 1)">="jedisConnectionFactory"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="messageListeners"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">map</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">entry </span><span style="color: rgba(255, 0, 0, 1)">key-ref</span><span style="color: rgba(0, 0, 255, 1)">="messageListenerAdapter"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.listener.ChannelTopic"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg </span><span style="color: rgba(255, 0, 0, 1)">value</span><span style="color: rgba(0, 0, 255, 1)">="WEB_SOCKET:LOTTERY"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">entry</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">map</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">property</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="messageListenerAdapter"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="org.springframework.data.redis.listener.adapter.MessageListenerAdapter"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg </span><span style="color: rgba(255, 0, 0, 1)">ref</span><span style="color: rgba(0, 0, 255, 1)">="redisMsgPubSubListener"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">constructor-arg</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="redisMsgPubSubListener"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="com.redis.pubsub.spring.RedisMsgPubSubListener"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

</beans>

如上的配置即配置了对 Redis 的链接。在配置类中的将 ChannelTopic 加入 IOC 容器。则在 Spring 启动时会在一个 RedisTemplate(一个对 Redis 的链接) 中设置的一个 channel,即 WEB_SOCKET:LOTTERY。 
在上述配置中,RedisMsgPubSubListener 是我们生成的,这个类即为核心监听类,RedisTemplate 接受到数据如何处理就是在该类中处理的。

附加上 Java 配置

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gfss.common.listener.CustomRedisMsgPubSubListener;

@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {

@Override
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> KeyGenerator keyGenerator() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> KeyGenerator() {
        @Override
        </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Object generate(Object target, Method method, Object... params) {
            StringBuilder sb </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Object obj : params) {
                sb.append(obj.toString());
            }
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> sb.toString();
        }
    };

}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span> CacheManager cacheManager(RedisTemplate&lt;?, ?&gt;<span style="color: rgba(0, 0, 0, 1)"> redisTemplate) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RedisCacheManager(redisTemplate);
}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span> RedisTemplate&lt;String, String&gt;<span style="color: rgba(0, 0, 0, 1)"> redisTemplate(RedisConnectionFactory factory) {
    StringRedisTemplate template </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisTemplate(factory);
    Jackson2JsonRedisSerializer</span>&lt;Object&gt; jackson2JsonRedisSerializer = <span style="color: rgba(0, 0, 255, 1)">new</span> Jackson2JsonRedisSerializer&lt;Object&gt;<span style="color: rgba(0, 0, 0, 1)">(
            Object.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
    ObjectMapper om </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    template.setValueSerializer(jackson2JsonRedisSerializer);
    template.afterPropertiesSet();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> template;
}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span> RedisTemplate&lt;String, Object&gt;<span style="color: rgba(0, 0, 0, 1)"> objectRedisTemplate(RedisConnectionFactory factory) {
    RedisTemplate</span>&lt;String, Object&gt; template = <span style="color: rgba(0, 0, 255, 1)">new</span> RedisTemplate&lt;String, Object&gt;<span style="color: rgba(0, 0, 0, 1)">();
    template.setConnectionFactory(factory);
    template.setKeySerializer(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisSerializer());
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> template;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">************ 配置redis发布订阅模式 ******************************</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> CustomRedisMsgPubSubListener customRedisMsgPubSubListener() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> CustomRedisMsgPubSubListener();
}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MessageListenerAdapter messageListenerAdapter(MessageListener messageListener) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MessageListenerAdapter(messageListener);
}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,
        MessageListenerAdapter messageListenerAdapter) {

    List</span>&lt;Topic&gt; collection = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;Topic&gt;<span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 普通订阅,订阅具体的频道</span>
    ChannelTopic channelTopic = <span style="color: rgba(0, 0, 255, 1)">new</span> ChannelTopic("WEB_SOCKET:LOTTERY"<span style="color: rgba(0, 0, 0, 1)">);
    collection.add(channelTopic);

    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">// 模式订阅,支持模式匹配订阅,*为模糊匹配符
    PatternTopic PatternTopic = new PatternTopic("WEB_SOCKET:*");
    collection.add(PatternTopic);
    // 匹配所有频道
    PatternTopic PatternTopicAll = new PatternTopic("*");
    collection.add(PatternTopicAll);</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">

    RedisMessageListenerContainer redisMessageListenerContainer </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> RedisMessageListenerContainer();
    redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
    redisMessageListenerContainer.addMessageListener(messageListenerAdapter, collection);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> redisMessageListenerContainer;
}

}

 

访问页面去调用发布者

http://localhost:8088/redis/redisMsgPubClientBySpring?cutomerId=all&msg= 你们好

订阅收到的消息

 

5. 拓展开发

  在分布式服务中,可以结合 WebSocket 与 Redis 的发布订阅模式相结合,解决 session 不能共享的问题。

  当业务处理完成之后,通过 Redis 的发布订阅模式,发布消息到每个订阅该频道的服务节点,然后由每个服务节点通过 key 寻找自己内存缓存中的 session,然后找到了就向客户端推消息,否则不处理。

Dubbo 只能传输可序列化的对象,Redis 只能缓存可序列化的对象,Dubbo 基于网络流(TCP),Redis 缓存的数据要存储在硬盘上,而 WebSocketSession 是没有实现序列化的,所以不能跨服务传递 WebSocketSession,也不能使用 Redis 存储 WebSocketSession,只能自定义一块缓存区。

6. 动态订阅频道

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.RedisSerializer;

import com.alibaba.fastjson.JSONObject;
import com.gfss.common.websocket.CustomWebSocketHandler;

/**

  • redis 发布订阅消息监听器
  • @ClassName: RedisMsgPubSubListener
  • @Description: TODO
  • @author OnlyMate
  • @Date 2018 年 8 月 22 日 上午 10:05:35

*/
public class CustomRedisMsgPubSubListener implements MessageListener {
private Logger logger = LoggerFactory.getLogger(CustomRedisMsgPubSubListener.class);

@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> CustomWebSocketHandler customWebSocketHandler;
@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationContext applicationContext;
@Autowired
</span><span style="color: rgba(0, 0, 255, 1)">private</span> RedisTemplate&lt;String, String&gt;<span style="color: rgba(0, 0, 0, 1)"> redisTemplate;
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 实例:
 *    JSONObject json = new JSONObject();
 *    json.put("cutomerId", notifyResult.getResult());
 *    json.put("resultCode", map.get("resultCode"));
 *    //向redis发布消息
 *    redisTemplate.convertAndSend(channelName, json);
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> message
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> pattern
 * @Throws
 * @Author: chetao
 * @Date: 2019年1月8日 下午10:40:21
 * </span><span style="color: rgba(128, 128, 128, 1)">@see</span><span style="color: rgba(0, 128, 0, 1)"> org.springframework.data.redis.connection.MessageListener#onMessage(org.springframework.data.redis.connection.Message, byte[])
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onMessage( <span style="color: rgba(0, 0, 255, 1)">final</span> Message message, <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] pattern ) {
    RedisSerializer</span>&lt;?&gt; serializer =<span style="color: rgba(0, 0, 0, 1)"> redisTemplate.getKeySerializer();
    logger.info(</span>"Message receive--&gt;pattern:{},message: {},{}"<span style="color: rgba(0, 0, 0, 1)">, serializer.deserialize(pattern),
            serializer.deserialize(message.getBody()), serializer.deserialize(message.getChannel()));
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> ("WEB_SOCKET:PAY_NOTIFY"<span style="color: rgba(0, 0, 0, 1)">.equals(serializer.deserialize(message.getChannel()))) {
        RedisMessageListenerContainer redisMessageListenerContainer </span>=<span style="color: rgba(0, 0, 0, 1)"> applicationContext
                .getBean(</span>"redisMessageListenerContainer", RedisMessageListenerContainer.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
        MessageListenerAdapter messageListenerAdapter </span>= applicationContext.getBean("messageListenerAdapter"<span style="color: rgba(0, 0, 0, 1)">,
                MessageListenerAdapter.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">List&lt;Topic&gt; collection = new ArrayList&lt;Topic&gt;();
        // 动态添加订阅主题
        ChannelTopic channelTopic = new ChannelTopic("WEB_SOCKET1:PAY_NOTIFY");
        collection.add(channelTopic);
        PatternTopic PatternTopic = new PatternTopic("WEB_SOCKET:*");
        collection.add(PatternTopic);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter, collection);</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
        <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 动态添加订阅主题</span>
        ChannelTopic channelTopic = <span style="color: rgba(0, 0, 255, 1)">new</span> ChannelTopic("WEB_SOCKET1:PAY_NOTIFY"<span style="color: rgba(0, 0, 0, 1)">);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter, channelTopic);
        PatternTopic PatternTopic </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> PatternTopic("WEB_SOCKET:*"<span style="color: rgba(0, 0, 0, 1)">);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter, PatternTopic);
    }

    JSONObject json </span>=<span style="color: rgba(0, 0, 0, 1)"> JSONObject.parseObject(message.toString());
    customWebSocketHandler.sendMessage(json.toJSONString());
}

}

上面两种动态订阅频道的方式都可以,本人已测试是可行的,可以结合自己的业务去拓展,如:临时订阅频道后退订频道