rabbitMq+spring实现延迟队列(适用订单超时未支付的问题)

什么是延迟队列


通俗一点说,延迟队列和我们生活中常用的定时器有点像,定时器会在指定的时间后响起,延迟队列则会在指定的时间后处理消息。延迟队列主要的应用场景有订单超时取消、超时自动评价等等。

实现原理


RabbitMQ给我们提供了TTL(Time-To-Live)和DLX (Dead-Letter-Exchange)这两个特性,(队列的TTL和消息的TTL如果同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就成为dead letter)使用RabbitMQ实现延迟队列利用的正是这两个特性。TTL用于控制消息的生存时间,如果超时,消息将变成Dead Letter。DLX用于配置死信队列,可以通过配置x-dead-letter-exchange和x-dead-letter-routing-key这两个参数来指定消息变成死信后需要路由到的交换器和队列。

图例

首先看看死信队列的图

rabbitMq+spring实现延迟队列(适用订单超时未支付的问题)_第1张图片

然后就是延迟队列,延迟队列本身就是死信队列的扩展

rabbitMq+spring实现延迟队列(适用订单超时未支付的问题)_第2张图片

代码实例




    
    

    
    
   

    
    
        
            
            
            
        
    

    
    
        
            
        
    

    
    

    
    
        
            
        
    


    
    

    
    

    
    
        
        
        
        
        
        
        
        
    

    

    
    
        
    



生产者:

这里可以单独对每个消息设置TTL(消息生存时间)

import com.rabbitmq.client.ConnectionFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class ProducerService {

/*    @Resource(name="mqConnectionFactory")
    private ConnectionFactory mqConnectionFactory;*/

    @Resource(name="orderAmqpTemplate")
    private AmqpTemplate orderAmqpTemplate;

    public void send(String msg) {
        orderAmqpTemplate.convertAndSend((Object) msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("10000");
                return message;
            }
        });
        System.out.println("Sent: " + msg);
    }

}

 

setExpiration是基于单个消息的超时设置,但是当我们先增加一条过期时间大(10000)的A消息进入,之后再增加一个过期时间小的(1000)消息B,并没有出现想象中的B消息先被消费,A消息后被消费,而是出现了当10000过去的时候,AB消息同时被消费,也就是B消息的消费被阻塞了。

为什么会出现这样的现象呢?
我们知道利用TTL DLX特性实现的方式,实际上在第一个延时队列C里面设置了dlx,生产者生产了一条带ttl的消息放入了延时队列C中,等到延时时间到了,延时队列C中的消息变成了死信,根据延时队列C中设置的dlx的exchange的转发规则,转发到了实际消费队列D中,当该队列中的监听器监听到消息时就会正式开始消费。那么实际上延时队列中的消息也是放入队列中的,队列满足先进先出,而延时大的消息A还没出队,所以B消息也不能顺利出队。
 

利用Rabbitmq的插件x-delay-message实现

为了解决上面的问题,Rabbitmq实现了一个插件x-delay-message来实现延时队列。

消费者:

这里可以手动消费

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class ConsumerService {
    @Resource(name="delayAmqpTemplate")
    private AmqpTemplate delayAmqpTemplate;

    public void recive() {
        System.out.println("Received: " + delayAmqpTemplate.receiveAndConvert());
    }
}

执行生产者操作

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

@Controller
@RequestMapping(value="/mqTest")
public class TestController {

    @Resource(name = "producerService")
    private ProducerService producerService;

    @Resource(name = "consumerService")
    private ConsumerService consumerService;


    @ResponseBody
    @RequestMapping(value="/producerTest")
    public void producerTest() throws Exception {
        String  s = "我是生产者测试";
        producerService.send(s);
    }

    @ResponseBody
    @RequestMapping(value="/consumerTest")
    public void consumerTest() throws Exception {
        consumerService.recive();
    }
}

通过监听死信队列的消息来进行消费

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class DelayTask implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String receivedMsg = new String(message.getBody(), "UTF-8");
            System.out.println("Received : " + receivedMsg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

参考:rubbitMq实战指南

 

你可能感兴趣的:(消息队列,rabbitMQ,java)