Redis implements message queue

1. Redis implements message queue principle

Commonly used message queues are RabbitMQ and ActiveMQ. Personally, I think this kind of message queue is too large and heavy. This article introduces the lightweight message queue service based on Redis.

Generally speaking, there are two modes of message queue, one is the publisher-subscribe mode, and the other is the producer and consumer mode. The message queue of Redis is also implemented based on these two principles.

  • Publisher and subscriber pattern: The publisher sends messages to the queue, and each subscriber receives the same message.
  • Producer and consumer mode: The producer puts the message into the queue, and multiple consumers listen together. Whoever grabs the resource first will take the message from the queue for processing. Note that each message can only be received by at most one consumer.

2. Redis message queue usage scenarios

In our project, the message queue is used to realize the service of short messages. Any module that needs to send short messages can directly call the short message service to complete the sending of short messages. For example, the user's system login registration SMS, the order system's successful order SMS, etc.

3. Implementing Redis message queue in SpringMVC

Because our short message only needs to be sent once, we use the producer and consumer mode of the message queue.

1. Introduce Maven dependencies

Introduce the corresponding maven dependency of Redis, which requires spring-data-redis and 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>

2. Configure redis producer-consumer mode

//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>

Main configuration instructions:

1) Serialization: Generally, we send a Java object defined by a message to Redis, and this object needs to be serialized. JdkSerializationRedisSerializer is used here:

<bean id="jdkSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />

2) Sender:

<bean id="sendMessage" class="com.djt.common.cache.redis.queue.SendMessage">
        <property name="redisTemplate" ref="redisTemplate"/>
</bean>

3) Listener:

<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: message listener
redis:listener-container: defines message monitoring, method: method for monitoring message execution, serializer: serialization, topic: monitoring topic (which can be understood as a queue name)

3. Code implementation

1) Define the SMS message object 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) 2. Define the message queue sending object 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) Send a message

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) Listen for messages

//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);
                }
            });
        }
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325524271&siteId=291194637