原文:https://my.oschina.net/u/3266761/blog/1926588

rabbitMq是受欢迎的消息中间件之一,相比其他的消息中间件,具有高并发的特性(天生具备高并发高可用的erlang语言编写),除此之外,还可以持久化,保证消息不易丢失,高可用,实现集群部署,提供灵活的路由和可靠性,可视化管理等等的优点。

相比于其他的消息队列,rabbitmq最大的特色就是加入了exchange(交换器)这个东西,AMQP协议中的核心思想就是生产者和消费者隔离,生产者从不直接将消息发送给队列。生产者通常不知道是否一个消息会被发送到队列中,只是将消息发送到一个交换机。先由Exchange来接收,然后Exchange按照特定的策略转发到Queue进行存储。同理,消费者也是如此。Exchange 就类似于一个交换机,转发各个消息分发到相应的队列中。

RabbitMQ常用的Exchange Type有三种:fanout、direct、topic。

fanout:把所有发送到该Exchange的消息投递到所有与它绑定的队列中。

direct:把消息投递到那些binding key与routing key完全匹配的队列中。

topic:将消息路由到binding key与routing key模式匹配的队列中。

言归正传,延时队列如何通过rabbitmq来实现呢?

分析:首先rabbitmq自己是不具备延时的功能的,除了使用官方提供的插件之外,我们还可以通过ttl(设置超时时间的方式)+ DLX(一个死信队列)的方式来实现 + Router(转发队列)

其中,ttl可以设置在消息上,也可以设置在队列上,设置在消息上可以提供更大的灵活性,但是如果同时设置超时时间的话,就取最小的超时时间为准。

此外,死信队列是一个普通的队列,它没有消费者,用来存储有超时时间信息的消息,并且可以设置当消息超时(ttl),转发到另一个指定队列(此处设置转发到router, 当发送消息之后(发送时,带上要延时的队列名称),等待消息超时,将消息转发到指定的Router队列。

最后,转发队列,用来接收死信队列超时消息,在接收到之后,消费者将消息解析,获取queueName,body,再向所获取的queueName队列发送一条消息,内容为body.

下面是代码:

生产者:

package cn.chinotan.service.delayQueueRabbitMQ;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @program: test
* @description: 生产者
* @author: xingcheng
* @create: 2018-08-12 12:33
**/
@Service
public class Producr {
private static final Logger LOGGER = LoggerFactory.getLogger(Producr.class); @Autowired
private AmqpTemplate amqpTemplate; public void send(String msg, long time, String delayQueueName) {
//rabbit默认为毫秒级
long times = time * 1000;
MessagePostProcessor processor = new MessagePostProcessor() { @Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration(String.valueOf(times));
return message;
}
};
// 拼装msg
msg = StringUtils.join(msg, ":", delayQueueName);
amqpTemplate.convertAndSend(MqConstant.MY_EXCHANGE, MqConstant.DEAD_LETTER_QUEUE, msg, processor);
}
}

消费队列1:

package cn.chinotan.service.delayQueueRabbitMQ;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service; import java.text.SimpleDateFormat;
import java.util.Date; /**
* @program: test
* @description: queueOne消费者
* @author: xingcheng
* @create: 2018-08-12 12:35
**/
@Service
public class MyQueueOneConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(MyQueueOneConsumer.class); @RabbitListener(queues=MqConstant.MY_QUEUE_ONE)
@RabbitHandler
public void process(String content) {
LOGGER.info("延迟时间到,queueOne开始执行 {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}

消费队列2:

package cn.chinotan.service.delayQueueRabbitMQ;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.text.SimpleDateFormat;
import java.util.Date; /**
* @program: test
* @description: queueTwo队列
* @author: xingcheng
* @create: 2018-08-12 12:35
**/
@Service
public class MyQueueTwoConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(MyQueueTwoConsumer.class);
@Autowired
private Producr producr; @RabbitListener(queues=MqConstant.MY_QUEUE_TWO)
@RabbitHandler
public void process(String content) {
LOGGER.info("延迟时间到,queueTwo开始执行 {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}

转发队列:

package cn.chinotan.service.delayQueueRabbitMQ;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.text.SimpleDateFormat;
import java.util.Date; /**
* @program: test
* @description: 转发队列
* @author: xingcheng
* @create: 2018-08-12 12:35
**/
@Service
public class TradeProcess {
private static final Logger LOGGER = LoggerFactory.getLogger(TradeProcess.class); @Autowired
private AmqpTemplate amqpTemplate; @RabbitListener(queues=MqConstant.MY_TRANS_QUEUE)
@RabbitHandler
public void process(String content) {
String msg = content.split(":")[0];
String delayQueueName = content.split(":")[1];
amqpTemplate.convertAndSend(MqConstant.MY_EXCHANGE, delayQueueName, msg);
LOGGER.info("进行转发 {}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}

队列配置:

package cn.chinotan.service.delayQueueRabbitMQ;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.HashMap;
import java.util.Map; /**
* @program: test
* @description: 延时队列rabbitMQ配置
* @author: xingcheng
* @create: 2018-08-12 12:27
**/
@Configuration
public class RabbitConfig { @Bean
public DirectExchange myExchange() {
return new DirectExchange(MqConstant.MY_EXCHANGE, true, false);
} @Bean
public Queue myQueueOne() {
return new Queue(MqConstant.MY_QUEUE_ONE, true, false, false);
} @Bean
public Queue myQueueTwo() {
return new Queue(MqConstant.MY_QUEUE_TWO, true, false, false);
}
@Bean
public Queue myTransQueue() {
return new Queue(MqConstant.MY_TRANS_QUEUE, true, false, false);
} @Bean
public Queue deadLetterQueue() {
Map<String, Object> map = new HashMap<>();
map.put("x-dead-letter-exchange", MqConstant.MY_EXCHANGE);
map.put("x-dead-letter-routing-key", MqConstant.MY_TRANS_QUEUE);
Queue queue = new Queue(MqConstant.DEAD_LETTER_QUEUE, true, false, false, map);
System.out.println("arguments :" + queue.getArguments());
return queue;
} @Bean
public Binding queueOneBinding() {
return BindingBuilder.bind(myQueueOne()).to(myExchange()).with(MqConstant.MY_QUEUE_ONE);
} @Bean
public Binding queueTwoBinding() {
return BindingBuilder.bind(myQueueTwo()).to(myExchange()).with(MqConstant.MY_QUEUE_TWO);
} @Bean
public Binding queueDeadBinding() {
return BindingBuilder.bind(deadLetterQueue()).to(myExchange()).with(MqConstant.DEAD_LETTER_QUEUE);
} @Bean
public Binding queueTransBinding() {
return BindingBuilder.bind(myTransQueue()).to(myExchange()).with(MqConstant.MY_TRANS_QUEUE);
}
}

队列常量配置:

package cn.chinotan.service.delayQueueRabbitMQ;

/**
* @program: test
* @description: rabbitMq常量
* @author: xingcheng
* @create: 2018-08-12 12:30
**/
public class MqConstant { public static final String MY_EXCHANGE = "my_exchange"; public static final String MY_QUEUE_ONE = "my_queue_one"; public static final String MY_QUEUE_TWO = "my_queue_two"; public static final String DEAD_LETTER_QUEUE = "dead_letter_queue"; public static final String MY_TRANS_QUEUE = "my_trans_queue"; }

测试延时controller:

package cn.chinotan.controller;

import cn.chinotan.service.delayQueueRabbitMQ.MqConstant;
import cn.chinotan.service.delayQueueRabbitMQ.Producr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import java.text.SimpleDateFormat;
import java.util.Date; /**
* @program: test
* @description: 延时队列启动类
* @author: xingcheng
* @create: 2018-08-12 15:41
**/
@RestController
@RequestMapping("/delayQueue")
public class DelayQueueController { private static final Logger LOGGER = LoggerFactory.getLogger(DelayQueueController.class); @Autowired
private Producr producr; @GetMapping("/send/{time}")
public String send(@PathVariable("time") int time){
LOGGER.info("{}秒后, 发送延迟消息,当前时间{}", time, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
producr.send("我是延时消息...", time, MqConstant.MY_QUEUE_TWO);
return "ok";
} }

测试结果:

 

rabbitMq实现延时队列的更多相关文章

  1. 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景

    前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...

  2. RabbitMq 实现延时队列-Springboot版本

    rabbitmq本身没有实现延时队列,但是可以通过死信队列机制,自己实现延时队列: 原理:当队列中的消息超时成为死信后,会把消息死信重新发送到配置好的交换机中,然后分发到真实的消费队列: 步骤: 1. ...

  3. 【日常摘要】- RabbitMq实现延时队列

    简介 什么是延时队列? 一种带有延迟功能的消息队列 过程: 使用场景 比如存在某个业务场景 发起一个订单,但是处于未支付的状态?如何及时的关闭订单并退还库存? 如何定期检查处于退款订单是否已经成功退款 ...

  4. rabbitmq实现延时队列(死信队列)

    基于队列和基于消息的TTL TTL是time to live 的简称,顾名思义指的是消息的存活时间.rabbitMq可以从两种维度设置消息过期时间,分别是队列和消息本身. 队列消息过期时间-Per-Q ...

  5. Rabbitmq的延时队列的使用

    配置: spring: rabbitmq: addresses: connection-timeout: username: guest password: guest publisher-confi ...

  6. RabbitMQ及延时队列

    一.简介 我用过RabbirMQ的发布订阅模式,以及一对一的延迟队列. 1.RabbitMQ的有消息确认机制,消费一条则队列中少一条,也有对应的消费到消息及认为是消费成功这样的模式,一般使用前者. 发 ...

  7. rabbitmq 安装延时队列插件rabbitmq-delayed-message-exchange

    1.下载rabbitmq-delayed-message-exchange(注意版本对应) 链接:https://github.com/rabbitmq/rabbitmq-delayed-messag ...

  8. IOS IAP 自动续订 之 利用rabbitmq延时队列自动轮询检查是否续订成功

    启用针对自动续期订阅的服务器通知: - 官方地址: - https://help.apple.com/app-store-connect/#/dev0067a330b - 相关字段, 相关类型地址:  ...

  9. 面试官:RabbitMQ过期时间设置、死信队列、延时队列怎么设计?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ我们经常的使用, ...

随机推荐

  1. Java基础教程:多线程基础(5)——倒计时器(CountDownLatch)

    Java基础教程:多线程基础(5)——倒计时器(CountDownLatch) 引入倒计时器 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种 ...

  2. 微服务Consul系列之服务部署、搭建、使用

    使用Consul解决了哪些问题 是否在为不同环境来维护不同项目配置而发愁 是否有因为配置的更改导致代码还要进行修改.发布因为客流量大了还要规避开高峰期等到半夜来发布 微服务架构下应用的分解业务系统与服 ...

  3. 树莓派3B安装arm64操作系统

    pi64 pi64基于Debian 9,地址如下https://github.com/bamarni/pi64 烧录过程还是用SDFormatter格式化,用Win32DiskImager写入即可,没 ...

  4. 【转】 嵌入式C语言编程中Inline函数的应用

    源地址:https://blog.csdn.net/vigour1000/article/details/9622037 有一段儿时间没写写经验笔记了,哎,也是自己这一段时间以来(其实最近一直是这个状 ...

  5. [BJOI2019] 删数 [dp转贪心结论+线段树]

    题面 传送门 思路 dp部分 以下称合法序列为原题面中可以删空的序列 这个是我在模拟考场上的思路 一开始我是觉得,这个首先可以写成一个dp的形式:$dp[i][j]$表示用$j$个数字填满了目标序列的 ...

  6. Eclipse+TestNG搭建接口自动化测试框架

    一.环境安装 1.前提 安装好jdk 配置好Java环境变量 安装Eclips 这些网上都有,就不再详细介绍. 资源分享链接:http://pan.baidu.com/s/1v9Fw6 2.安装Tes ...

  7. 工具中修改设置Default VM Arguments

    转自:https://www.cnblogs.com/zouhao/p/6513177.html

  8. R画柱形图和箱线图

    数据格式如下 gene_id Sham-1 Sham-2 Sham-3 Sham-4 Sham-5 Rep-1h-1 Rep-1h-2 Rep-1h-3 Rep-1h-4 Rep-1h-5 Rep-3 ...

  9. linux ubuntu 网卡配置---固定IP

    需要修改/etc/network/interfaces和/etc/resolvconf/resolv.conf.d/base两个文件. 1) /etc/network/interfaces文件: 首先 ...

  10. Prometheus入门到放弃(1)之Prometheus安装部署

    规划: IP 角色 版本 10.10.0.13 prometheus-server 2.10 10.10.0.11 node_exporter 0.18.1 10.10.0.12 node_expor ...