当前位置: 首页 > news >正文

Spring Data Redis—Pub/Sub(附Web项目源码)

原文:https://blog.csdn.net/u010629028/article/details/39893013

 

 

一、发布和订阅机制

  当一个客户端通过 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

相关文章:

  • 用面对对象方式定tab标签
  • Android 开发小知识点收集(随时更新)
  • 第二阶段冲刺8
  • 用shell脚本写的一个9*9乘法表
  • 导入项目后遇到页面报错如何解决
  • Ubuntu: how to md5sum (适用macOS)
  • Linux 磁盘配额 quota
  • crontab笔记
  • 互联网一线大厂都在用的Java架构师知识体系
  • linux 安全
  • centos中mysql,和配置ansible遇到的错误
  • 博客版权问题
  • Graph database_neo4j 底层存储结构分析(1)
  • python_bomb----数据类型总结
  • Advanced Bash-Shell Guide(Version 10) 学习笔记三
  • [译]Python中的类属性与实例属性的区别
  • 【挥舞JS】JS实现继承,封装一个extends方法
  • 【前端学习】-粗谈选择器
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 4个实用的微服务测试策略
  • 5、React组件事件详解
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • CSS实用技巧
  • JavaScript中的对象个人分享
  • Redis的resp协议
  • 阿里云前端周刊 - 第 26 期
  • 技术:超级实用的电脑小技巧
  • 理清楚Vue的结构
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 面试总结JavaScript篇
  • 排序算法之--选择排序
  • 七牛云假注销小指南
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何选择开源的机器学习框架?
  • 微信支付JSAPI,实测!终极方案
  • 云大使推广中的常见热门问题
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • (06)金属布线——为半导体注入生命的连接
  • (Oracle)SQL优化技巧(一):分页查询
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (强烈推荐)移动端音视频从零到上手(上)
  • (三)模仿学习-Action数据的模仿
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (一)appium-desktop定位元素原理
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • **PHP分步表单提交思路(分页表单提交)
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .Net Web项目创建比较不错的参考文章
  • .net 中viewstate的原理和使用
  • .NET6实现破解Modbus poll点表配置文件
  • /etc/sudoer文件配置简析