zoukankan      html  css  js  c++  java
  • Spring mvc Data Redis—Pub/Sub(附Web项目源码)

    一、发布和订阅机制

      当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher)。

      而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE 命令接收信息的时候,我们称这个客户端为订阅者(subscriber)。

    为了解耦发布者(publisher)和订阅者(subscriber)之间的关系,Redis 使用了 channel (频道)作为两者的中介 —— 发布者将信息直接发布给 channel ,而 channel 负责将信息发送给适当的订阅者,发布者和订阅者之间没有相互关系,也不知道对方的存在

    注意:Redis的Pub Sub功能(或许是暂时)不支持持久化,意思就是消息在管道中是即发即失的,Subscriber端一收到消息,消息即从管道中删除。所以如果是对消息的准确性要求比较高或者是有持久化的需求,Redis就不是那么合适了,期待以后的版本加入持久化功能。

    二、Pub/Sub的作用

             其实从Pub/Sub的机制来看,它更像是一个广播系统,多个Subscriber可以订阅多个Channel,多个Publisher可以往多个Channel中发布消息。可以这么简单的理解:

    Subscriber:收音机(只不过这个收音机可以收到多个频道,并以队列方式显示)

    Publisher:电台(电台可以往不同的FM频道中发消息)

    Channel:不同频率的FM频道

    所以根据这个理解,那么我觉得有几种用法是比较可取的:

      1.一个Publisher,多个Subscriber:

      如下图所示,可以作为消息队列或者消息管道。

      主要应用:通知、公告。

      2.多个Publisher,一个Subscriber:

      可以将PubSub做成独立的HTTP接口,各应用程序作为Publisher向Channel中发送消息,Subscriber端收到消息后执行相应的业务逻辑,比如写数据库,显示等等。

      主要应用:排行榜、投票、计数。

    3.多个Publisher,多个Subscriber

    图就不上了,故名思议,就是可以向不同的Channel中发送消息,由不同的Subscriber接收。

    主要应用:群聊、聊天。

    可参考Spring data redis主页的开源项目retwisj。

    Github地址:https://github.com/spring-projects/spring-data-keyvalue-examples/tree/master/retwisj

     

    从上述几种用法来看,根据不同的限制条件,限制Publisher、Subscriber和Channel的数量,可以实现不同的功能,其实完全可以把Pub/Sub理解为Socket编程,用Socket也可以实现上述功能,但是Redis提供了相应的封装和底层实现,不管是安全性、健壮性的等各方面都有不错的表现,以及未来的一些拓展,个人觉得Redis是个不错的选择。

     

    三、Demo演示:

    因为我的上一篇博客Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解,已经演示了Spring Data Redis的基本配置和使用,所以这里就只贴上Pub/Sub的重要代码,读者可以阅读上篇博客或者下载源码。

    Pub/Sub配置(XMl):

    复制代码
     1 <!-- SDR Pub/Sub配置 -->
     2     <!-- SubServiceImpl是实现了MessageListener接口的类,MessageListener接口中定义了onMessage方法,也就是接收消息的方法,每当Channel中有消息,onMessage方法会被自动调用, -->
     3     <bean id="messageListener" class="com.chr.service.impl.SubServiceImpl">
     4     </bean>
     5     
     6     <!-- 可以有多个messageListener,每个messageListener必须注册到RedisMessageListenerContainer中,读者可参阅API文档 -->
     7     <bean id="messageContainer"
     8         class="org.springframework.data.redis.listener.RedisMessageListenerContainer"
     9         destroy-method="destroy">
    10         <property name="connectionFactory" ref="connectionFactory" />
    11         <!--<property name="taskExecutor"> <bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"> 
    12 <property name="poolSize" value="3"></property> </bean> </property>
    13 此处可以定义Executor,参阅java.util.concurrent.Executor-->
    14         <property name="messageListeners">
    15             <map>
    16                 <entry key-ref="messageListener">
    17                     <ref bean="channelTopic" />
    18                 </entry>
    19             </map>
    20         </property>
    21     </bean>
    22 
    23     <!-- Channel设置 -->
    24     <bean id="channelTopic" class="org.springframework.data.redis.listener.ChannelTopic">
    25         <constructor-arg value="user:topic" />
    26     </bean>
    复制代码

    在代码中可以看到subServiceImpl实现类被手动注册到配置文件中,这样可能会使代码混乱,并且会带来一些问题,比如需要使用注解自动注入rankService,但是因为Spring配置中,XML的优先级大于Annotation,所以subServiceImpl中的rankService不能被@Autowired。

    那么解决办法有两种:

      1.在配置文件中(messageLisener bean前)加入:

    <!-- 类扫描器 -->
    
        <context:component-scan base-package="com.songod.service" />

    这样Spring会先扫描Annotation,创建rankService bean,之后再注入messageLisener。

     

      2.在messageContainer bean中,只注入connectionFactory,不注入messageLisener和channelTopic。 之后在Controller中手动注入,调用addMessageListener(MessageListener listener, Topic topic)方法手动注入,但是注意只能注入一次,可以设置Flag判断。

    PubServiceImpl:

    复制代码
     1 @Service
     2 public class PubServiceImpl implements PubService {
     3     @Resource(name="stringRedisTemplate")
     4     private  StringRedisTemplate stringRedisTemplate;
     5     
     6     private String channelTopic = "user:topic";
     7     
     8     /*发布消息到Channel*/
     9     public void Publisher(String message) {
    10         stringRedisTemplate.convertAndSend(channelTopic, message);
    11     }
    12 }
    复制代码

    我这里用的是StringRedisTemplate,读者可以使用RedisTemplate设置其它序列化方式,可以看我的上一篇博客。

    SubServiceImpl:

    复制代码
    public class SubServiceImpl implements SubService {
        @Autowired
        private ChannelTopic channelTopic;
    
        private MessageList messageList = new MessageList();
    
        public void onMessage(Message message, byte[] pattern) {
            System.out.println(message.toString() + "  " + channelTopic.getTopic());
            messageList.add(message.toString());
        }
    
        public MessageList getMessageList() {
            return messageList;
        }
    }
    复制代码

    主要是onMessage方法,可以在此方法中将message传入其它业务逻辑中进行处理。

     

    四、Demo运行:

    Publish:

    Subscrib:

    五、项目源码:

     redis-web-pubsub

  • 相关阅读:
    Windows 科研软件推荐
    有关Python 包 (package) 的基本知识
    《Using Python to Access Web Data》Week4 Programs that Surf the Web 课堂笔记
    Coursera助学金申请模板
    《Using Databases with Python》 Week2 Basic Structured Query Language 课堂笔记
    Jupyter 解决单个变量输出问题
    解决 pandas 中打印 DataFrame 行列显示不全的问题
    《Using Python to Access Web Data》 Week3 Networks and Sockets 课堂笔记
    缓存击穿及解决方案
    jvm垃圾收集器
  • 原文地址:https://www.cnblogs.com/fx2008/p/4114998.html
Copyright © 2011-2022 走看看