基于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> <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"
基于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> <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> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="300" /> <property name="maxTotal" value="60000" /> <property name="testOnBorrow" value="true" /> </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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
主要的配置说明:
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 { private Integer smsId; private String mobile; private Byte type; private Date createDate; private Date processTime; private Byte status; private String content; ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
2.定义消息队列发送对象SendMessage
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); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
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配置一样
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
4.监听消息
@Component("smsMessageDelegateListener")public class SmsMessageDelegateListener { @Autowired private SmsQueueService smsQueueService; 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); } }); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
4.总结
下面使用一张流程图,来总结Redis消息队列和短信服务。