RabbitMQ延迟消息学习
准备做一个禁言自动解除的功能,立马想到了订单的超时自动解除,刚好最近在看RabbitMQ的实现,于是想用它实现,查询了相关文档发现确实可以实现,动手编写了这篇短文。
准备工作
1、Erlang安装请参考windows下安装Erlang
2、mq安装晴参考RabbitMQ安装
3、延迟消息插件安装rabbitmq_delayed_message_exchange
    #插件下载地址(选择与mq版本匹配的插件版本)
    http://www.rabbitmq.com/community-plugins.html
    #安装命令如下(在安装目录sbin下执行如下命令)
    rabbitmq-plugins enable rabbitmq_delayed_message_exchange
创建项目
我选择的是在springboot中集成RabbitMQ,配置相对简单很多。


项目创建好后,在application.properties中加入RabbitMQ参数:
#RabbitMQ config
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#Custom config
rabbitmq.exchange=test_exchange
rabbitmq.queue=test_queue_1
定义ConnectionFactory和RabbitTemplate
    package com.xsh.mq.config;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitMqConfig {
    private String host;
    private int port;
    private String userName;
    private String password;
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host,port);
        cachingConnectionFactory.setUsername(userName);
        cachingConnectionFactory.setPassword(password);
        cachingConnectionFactory.setVirtualHost("/");
        cachingConnectionFactory.setPublisherConfirms(true);
        return cachingConnectionFactory;
    }
    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
        return rabbitTemplate;
    }
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
Exchange和Queue配置
    package com.xsh.mq.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
 * 配置队列
 */
@Configuration
public class QueueConfig {
    @Value("${rabbitmq.exchange}")
    private String exchangeName;
    @Value("${rabbitmq.queue}")
    private String queueName;
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
       //使用的是CustomExchange,不是DirectExchange,另外CustomExchange的类型必须是x-delayed-message
        return new CustomExchange(exchangeName, "x-delayed-message",true, false,args);
    }
    @Bean
    public Queue queue() {
        Queue queue = new Queue(queueName, true);
        return queue;
    }
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(delayExchange()).with(queueName).noargs();
    }
}
消息发送
    package com.xsh.mq.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class MessageServiceImpl {
    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
    /**
     * rabbitMQ模板
     */
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Value("${rabbitmq.exchange}")
    private String exchangeName;
    /**
     * 发送消息
     * @param queueName 队列名称
     * @param msg 消息内容
     * @param delay 延迟时长 默认3秒
     */
    public void sendMsg(String queueName,String msg,Integer delay) {
        if(null == delay){
            delay = 3000;
        }
        logger.info("》》》》发送消息");
        Integer finalDelay = delay;
        rabbitTemplate.convertAndSend(exchangeName, queueName, msg, message -> {
            //必须添加header x-delay
            message.getMessageProperties().setHeader("x-delay", finalDelay);
            return message;
        });
    }
}
这里发送消息我定义了一个延迟参数,传入的延迟是多少,消息就延迟多少,方便消息延迟不一样
消费消息
    package com.xsh.mq.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class MessageReceiver {
    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(MessageReceiver.class);
    @RabbitListener(queues = "${rabbitmq.queue}")
    public void receive(String msg) {
        logger.info("收到消息:{}", msg);
    }
}
测试发送接收
先运行springboot项目,然后编写单元测试用例
      package com.xsh.mq;
  import com.xsh.mq.service.MessageServiceImpl;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.test.context.junit4.SpringRunner;
  @RunWith(SpringRunner.class)
  @SpringBootTest
  public class MqApplicationTests {
      @Test
      public void contextLoads() {
      }
      @Autowired
      private MessageServiceImpl messageService;
      @Value("${rabbitmq.queue}")
      private String queueName;
      @Test
      public void send() {
          messageService.sendMsg(queueName, "delayMsg2", 1000 * 60 * 2);
          messageService.sendMsg(queueName, "delayMsg1", 1000 * 60);
          messageService.sendMsg(queueName, "delayMsg3", 1000 * 60*3);
      }
  }
这里我发送了三条延迟消息,控制台结果如图:

消费者接收到的消息为:

从执行结果来看,demo基本实现,RabbitMQ其他细节还有待继续看。
参考文章:Scheduling Messages with RabbitMQ
RabbitMQ延迟消息学习的更多相关文章
- Spring Boot RabbitMQ 延迟消息实现完整版
		概述 曾经去网易面试的时候,面试官问了我一个问题,说 下完订单后,如果用户未支付,需要取消订单,可以怎么做 我当时的回答是,用定时任务扫描DB表即可.面试官不是很满意,提出: 用定时任务无法做到准实时 ... 
- RabbitMQ延迟消息的延迟极限是多少?
		之前在写Spring Cloud Stream专题内容的时候,特地介绍了一下如何使用RabbitMQ的延迟消息来实现定时任务.最近正好因为开发碰到了使用过程中发现,延迟消息没有效果,消息直接就被消费了 ... 
- RabbitMQ延迟消息:死信队列 | 延迟插件 | 二合一用法+踩坑手记+最佳使用心得
		前言 前段时间写过一篇: # RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得 很多人加了我好友,说很喜欢这篇文章,也问了我一些问题. 因为最近工作比较忙, ... 
- RabbitMQ延迟消息队列实现定时任务完整代码示例
- Spring Cloud Stream 使用延迟消息实现定时任务(RabbitMQ)
		应用场景 通常在应用开发中我们会碰到定时任务的需求,比如未付款订单,超过一定时间后,系统自动取消订单并释放占有物品. 许多同学的第一反应就是通过spring的schedule定时任务轮询数据库来实现, ... 
- 15-EasyNetQ之对延迟消息插件的支持
		RabbitMQ延迟消息插件仍然在实验阶段.你使用这个功能要自担风险. RabbitMQ延迟消息插件为RabbitMQ增加了新的交换机类型,允许延时消息投递. EasyNetQ为交换机通过定义一种新的 ... 
- EasyNetQ使用(八)【对延迟消息插件的支持,自动订阅者】
		RabbitMQ延迟消息插件仍然在实验阶段.你使用这个功能要自担风险. RabbitMQ延迟消息插件为RabbitMQ增加了新的交换机类型,允许延时消息投递. EasyNetQ为交换机通过定义一种新的 ... 
- rabbitmq学习(二):rabbitmq(消息队列)的作用以及rabbitmq之直连交换机
		前言 上篇介绍了AMQP的基本概念,组成及其与rabbitmq的关系.了解了这些东西后,下面我们开始学习rabbitmq(消息队列)的作用以及用java代码和rabbitmq通讯进行消息发布和接收.因 ... 
- rabbitmq的延迟消息队列实现
		第一部分:延迟消息的实现原理和知识点 使用RabbitMQ来实现延迟任务必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求. 消息的TTL(Tim ... 
随机推荐
- Python - 集成开发环境Pycharm的使用方法和技巧
			PyCharm HomePage:PyCharm 我的Pycharm,我做主 Getting Started with PyCharm Pycharm使用技巧 Documentation & ... 
- 关于如何使`(a === 1 && a === 2 && a === 3)`返回`true`问题的思考
			看见这个面试题目,第一反应就是在变量a取值时进行了一些改变,那就要用getter,关于存取器的介绍可以看这里 var temp = 1; Object.defineProperty(window, ' ... 
- Angular使用总结 --- 通过指令动态添加组件
			之前自己写的公共组件,都是会先引入,需要调起的时候再通过service控制公共组件状态.值.回调函数什么的.但是有一些场景不适合这种方式,还是动态添加组件更加好.通过写过的一个小组件来总结下. 创建组 ... 
- OC学习1——基本数据类型
			1.OC是在C语言的基础上进行扩展的一种面向对象的编程语言.很多基础知识都和C语言中的非常类似.首先介绍一下OC中的基本数据类型,整体框架如下图: 2.自动数据类型转换顺序:short --> ... 
- LeetCode--No.015   3Sum
			15. 3Sum Total Accepted: 131800 Total Submissions: 675028 Difficulty: Medium Given an array S of n i ... 
- 内存管理cpuset,mempolicy[原理]
			介绍cpuset,mbind,set_mempolicy在内存管理上的应用 change log :确定先从mempolicy的man 手册翻译开始研究,计划如下 .先从man手册入手,通过实现mem ... 
- 课程四(Convolutional Neural Networks),第一周(Foundations of Convolutional Neural Networks) —— 2.Programming assignments:Convolutional Model: step by step
			Convolutional Neural Networks: Step by Step Welcome to Course 4's first assignment! In this assignme ... 
- 希尔排序——Shell Sort
			前言: 数据序列1: 13-17-20-42-28 利用插入排序,13-17-20-28-42. Number of swap:1;数据序列2: 13-17-20-42-14 利用插入排序,13-14 ... 
- delete attempted to return null from a method with a primitive return type (int)
			今天被自己给蠢死了 今天在代码中遇到这个错误, 百度翻译一下:映射方法,从一org.system.mapper.child.chmorganizationexaminationmapper.delet ... 
- 偏流角(Draft Angle)在等距螺旋中的作用
			劳动改变人,思维改变世界.我们可以接着聊螺旋线了. 在飞行程序设计中,偏流角(Draft Angle简写为DA)通常指得是受侧风影响航向偏移的最大角度.用速度向量来表示时,是图1中的三角形关系: 图1 ... 
