Redis 发布与订阅 消息
基于Redis消息队列-实现短信服务化
1.Redis实现消息队列原理
常用的消息队列有RabbitMQ,ActiveMQ,个人觉得这种消息队列太大太重,本文介绍下基于Redis的轻量级消息队列服务。
一般来说,消息队列有两种模式,一种是发布者订阅模式,另外一种是生产者和消费者模式。Redis的消息队列,也是基于这2种原理的实现。
发布者和订阅者模式:发布者发送消息到队列,每个订阅者都能收到一样的消息。
生产者和消费者模式:生产者将消息放入队列,多个消费者共同监听,谁先抢到资源,谁就从队列中取走消息去处理。注意,每个消息只能最多被一个消费者接收。
2.Redis消息队列使用场景
在我们的项目中,使用消息队列来实现短信的服务化,任何需要发送短信的模块,都可以直接调用短信服务来完成短信的发送。比如用户系统登录注册短信,订单系统的下单成功的短信等。
3.SpringMVC中实现Redis消息队列
因为我们短信只需要发送一次,所以我们使用的是消息队列的生产者和消费者模式。
3.1引入Maven依赖
引入Redis相应的maven依赖,这里需要spring-data-redis和jedis
//pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.5.1</version>
</dependency>
3.2配置redis生成者消费者模式
//applicationContext-redis.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:redis="http://www.springframework.org/schema/redis"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-34.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis-1.0.xsd">
<description>spring-data-redis配置</description>
<bean id="redisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="usePool" value="true"></property>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"></property>
</bean>
<bean id="jdkSerializer"
class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
<bean id="smsMessageListener"
class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="smsMessageDelegateListener" />
<property name="serializer" ref="jdkSerializer" />
</bean>
<bean id="sendMessage" class="com.djt.common.cache.redis.queue.SendMessage">
<property name="redisTemplate" ref="redisTemplate"/>
</bean>
<redis:listener-container>
<redis:listener ref="smsMessageListener" method="handleMessage"
serializer="jdkSerializer" topic="sms_queue_web_online" />
</redis:listener-container>
<!-- jedis -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="300" /> <!-- 最大能够保持idel状态的对象数 -->
<property name="maxTotal" value="60000" /> <!-- 最大分配的对象数 -->
<property name="testOnBorrow" value="true" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1" value="${redis.host}" />
<constructor-arg index="2" value="${redis.port}" type="int" />
</bean>
</beans>
主要的配置说明:
1.序列化:一般我们向Redis发送一个消息定义的Java对象,这个对象需要序列化。这里使用JdkSerializationRedisSerializer:
<bean id="jdkSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
2.发送者:
<bean id="sendMessage" class="com.djt.common.cache.redis.queue.SendMessage">
<property name="redisTemplate" ref="redisTemplate"/>
</bean>
3.监听者:
<bean id="smsMessageListener"
class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
<property name="delegate" ref="smsMessageDelegateListener" />
<property name="serializer" ref="jdkSerializer" />
</bean>
<redis:listener-container>
<redis:listener ref="smsMessageListener" method="handleMessage"
serializer="jdkSerializer" topic="sms_queue_web_online" />
</redis:listener-container>
smsMessageListener:消息监听器
redis:listener-container:定义消息监听,method:监听消息执行的方法,serializer:序列化,topic:监听主题(可以理解为队列名称)
3.3代码实现
1.定义短信消息对象SmsMessageVo
public class SmsMessageVo implements Serializable {
//id
private Integer smsId;
//手机号
private String mobile;
//类型,1:验证码 2:订单通知
private Byte type;
//短信创建时间
private Date createDate;
//短信消息处理时间
private Date processTime;
//短信状态,1:未发送 2:发送成功 3:发送失败
private Byte status;
//短信内容
private String content;
//省略setter和getter方法
...
2.定义消息队列发送对象SendMessage
//SendMessage.java
public class SendMessage {
private RedisTemplate<String, Object> redisTemplate;
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void sendMessage(String channel, Serializable message) {
redisTemplate.convertAndSend(channel, message);
}
}
3.发送消息
String smsContent = templateToContent(template.getContent(),
regMsgCode);
SmsMessageVo smsMessageVo = new SmsMessageVo();
smsMessageVo.setMobile(mobile);
smsMessageVo.setType((byte) SmsType.VERIFICATION.getType());
smsMessageVo.setChannelId(1);
smsMessageVo.setContent(smsContent);
smsMessageVo.setCreateDate(new Date());
smsMessageVo.setStatus((byte) SmsSendStatus.TO_SEND.getType());
smsMessageVo.setTemplateId(1);
//先把待发送的短信存入数据库
SmsQueue smsQueue = new SmsQueue();
BeanUtils.copyProperties(smsQueue, smsMessageVo);
smsQueueService.addSmsQueue(smsQueue);
//异步发送短信到redis队列
sendMessage.sendMessage(Constants.REDIS_QUEUE_SMS_WEB, smsMessageVo);
//Constants.REDIS_QUEUE_SMS_WEB = "sms_queue_web_online",和applicationContext-redis中topic配置一样
4.监听消息
//SmsMessageDelegateListener.java
@Component("smsMessageDelegateListener")
public class SmsMessageDelegateListener {
@Autowired
private SmsQueueService smsQueueService;
//监听Redis消息
public void handleMessage(Serializable message){
if(message instanceof SmsMessageVo){
SmsMessageVo messageVo = (SmsMessageVo) message;
//发送短信
SmsSender smsSender = SmsSenderFactory.buildEMaySender();
smsSender.setMobile(messageVo.getMobile());
smsSender.setContent(messageVo.getContent());
boolean sendSucc = false;
//判断短信类型
//验证码短信
if(messageVo.getType() == (byte)SmsType.VERIFICATION.getType()){
sendSucc = smsSender.send();
}
if(!sendSucc){
return;
}
// 异步更新短信表状态为发送成功
final Integer smsId = messageVo.getSmsId();
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
public void run() {
SmsQueue smsQueue = new SmsQueue();
smsQueue.setSmsId(smsId);
smsQueue.setStatus((byte)SmsSendStatus.SEND.getType());
smsQueue.setProcessTime(new Date());
smsQueueService.updateSmsQueue(smsQueue);
}
});
}
}
}
Redis 发布与订阅 消息的更多相关文章
- 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ
本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...
- RBMQ发布和订阅消息
RBMQ发布和订阅消息 exchange 参考翻译自: RabbitMQ官网 生产者并非将消息直接发送到queue,而是发送到exchange中,具体将消息发送到特定的队列还是多个队列,或者是丢弃,取 ...
- Redis的高级应用-事务处理、持久化、发布与订阅消息、虚拟内存使用
三.事务处理 Redis的事务处理比较简单.只能保证client发起的事务中的命令可以连续的执行,而且不会插入其他的client命令,当一个client在连接 中发出multi命令时,这个连接就进入一 ...
- Redis - 发布和订阅
一.概述 1). 发布和订阅是一种消息通信模式. 2). 优点:使消息订阅者和消息发布者耦合度降低,类似设计模式中的观察者模式. 二.发布和订阅 订阅命令: // 订阅一个或多个频道 // 返回值:v ...
- redis发布与订阅
发布与订阅 除了实现任务队列外, Redis还提供了一组命令可以让开发者实现"发布/订阅"(publish/subscribe)模式. "发布/订阅"模式同样可 ...
- redis 发布和订阅实现
参考文献 15天玩转redis -- 第九篇 发布/订阅模式 <Redis设计与实现> 命令简介 在redis用户手册中,跟发布订阅相关的命令有如下的六个: PSUBSCRIBE PUBL ...
- redis 发布与订阅原理分析
前言:用了redis也有一段时间了,但是发布与订阅的使用频率也不高,趁着这次空闲,深究下redis的发布与订阅模式. 一.订阅频道和信息发布 功能说明:Redis 的 SUBSCRIBE 命令可以让客 ...
- Redis——发布和订阅
发布与订阅(又称pub/sub),订阅者(listener)负责订阅频道(channel),发送者(publisher)负责向频道发送二进制字符串消息(binary string message).每 ...
- EasyNetQ使用(六)【多态发布和订阅,消息版本控制】
你能够订阅一个接口,然后发布基于这个接口的实现. 让我们看下一个示例.我有一个接口IAnimal和两个实现Cat和Dog: public interface IAnimal { string Name ...
随机推荐
- WPF Demo6
通知项熟悉.数据绑定 using System.ComponentModel; namespace Demo6 { /// <summary> /// 通知项属性 /// </sum ...
- BASIC-11_蓝桥杯_十六进制转十进制
注意事项: 1.长数据注意选择long long类型,用%ldd输出,防止数据溢出; 示例代码: #include <stdio.h>#include <string.h>#i ...
- Excel数组排序+图片统一大小
Sub 图片调整合适大小() ' Debug.Print ActiveWorkbook.Name 图片显示比例 = 0.9 '1为顶满单元格 Dim wb As Workbook, sh As Wor ...
- java操作Excel之POI(1)
一.新建工作簿.sheet.单元格 public static void main(String[] args) throws Exception { Workbook wb = new HSSFWo ...
- SpringMVC中session的使用
SpringMVC中仍然可以使用传统方式使用session /** * 使用session - 传统方式 */ @RequestMapping("/hello13.action") ...
- class<T>和 class<?>类型 有什么区别
平时看java源代码的时候,如果碰到泛型的话,我想? T K V E这些是经常出现的,但是有时想不起来代表什么意思,今天整理下: ? 表示不确定的java类型. T 表示java类型. K V 分别代 ...
- 拼图游戏js
实现算法: 1. JavaScript动态生成拼图:通过生成16个div,且除最后一个div不使用背景图片以外,其他div都设置拼图图片为背景.然后通过调整background-position来实现 ...
- ubuntu 常用命令集锦
一.文件/文件夹管理 ls 列出当前目录文件(不包括隐含文件) ls -a 列出当前目录文件(包括隐含文件) ls -l 列出当前目录下文件的详细信息 cd .. 回当前目录的上一级目录 cd - 回 ...
- linux中的ftp命令
转载至:https://www.cnblogs.com/mingforyou/p/4103022.html 一.ftp的get命令和mget命令有何不同? get一次只下载一个文件:mget一次可以下 ...
- 一个PHPer的规划
前言:学PHP过时了吗?PHP开发人员如何快速成长?怎么进行职业规划?特别是近几年非常火热的人工智能,机器学习,区块链技术等等,这多少会带动一些人盲目跟风,迷茫等,下面是PHP大牛魏永强带来的一篇根据 ...