Spring Boot RabbitMQ 延迟消息实现完整版示例

概述

曾经去网易面试的时候,面试官问了我一个问题,说

下完订单后,如果用户未支付,需要取消订单,可以怎么做

我当时的回答是,用定时任务扫描DB表即可。面试官不是很满意,提出:

用定时任务无法做到准实时通知,有没有其他办法?

我当时的回答是:

可以用队列,订单下完后,发送一个消息到队列里,并指定过期时间,时间一到,执行回调接口。

面试官听完后,就不再问了。其实我当时的思路是对的,只不过讲的不是很专业而已。专业说法是利用 延迟消息 。

其实用定时任务,确实有点问题,原本业务系统希望10分钟后,如果订单未支付,就马上取消订单,并释放商品库存。但是一旦数据量大的话,就会加长获取未支付订单数据的时间,部分订单就做不到10分钟后取消了,可能是15分钟,20分钟之类的。这样的话,库存就无法及时得到释放,也就会影响成单数。而利用延迟消息,则理论上是可以做到按照设定的时间,进行订单取消操作的。

目前网上关于使用RabbitMQ实现延迟消息的文章,大多都是讲如何利用RabbitMQ的死信队列来实现,实现方案看起来都很繁琐复杂,并且还是使用原始的RabbitMQ Client API来实现的,更加显得啰嗦。

Spring Boot 已经对RabbitMQ Client API进行了包装,使用起来简洁很多,下面详细介绍一下如何利用 rabbitmq_delayed_message_exchange 插件和Spring Boot来实现延迟消息。

软件准备

erlang

本文使用的版本是:Erlang 20.3

RabbitMQ

本文使用的是 window 版本的RabbitMQ,版本号是:3.7.4

rabbitmq_delayed_message_exchange插件

插件下载地址:http://www.rabbitmq.com/community-plugins.html

打开网址后,ctrl + f,搜索 rabbitmq_delayed_message_exchange 。

千万记住,一定选好版本号,由于我使用的是RabbitMQ 3.7.4,因此对应的 rabbitmq_delayed_message_exchange 插件也必须选择3.7.x的。

如果没有选对版本,在使用延迟消息的时候,会遇到各种各样的奇葩问题,而且网上还找不到解决方案。我因为这个问题,折腾了整整一个晚上。请牢记,要选对插件版本。

下载完插件后,将其放置到RabbitMQ安装目录下的 plugins 目录下,并使用如下命令启动这个插件:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

如果启动成功会出现如下信息:

The following plugins have been enabled:   rabbitmq_delayed_message_exchange

启动插件成功后,记得重启一下RabbitMQ,让其生效。

集成RabbitMQ

这个就非常简单了,直接在maven工程的pom.xml文件中加入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

Spring Boot的版本我使用的是 2.0.1.RELEASE .

接下来在 application.properties 文件中加入redis配置:

spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

定义ConnectionFactory和RabbitTemplate

也很简单,代码如下:

package com.mq.rabbitmq;

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.mq.rabbitmq;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class QueueConfig {

  @Bean
  public CustomExchange delayExchange() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    return new CustomExchange("test_exchange", "x-delayed-message",true, false,args);
  }

  @Bean
  public Queue queue() {
    Queue queue = new Queue("test_queue_1", true);
    return queue;
  }

  @Bean
  public Binding binding() {
    return BindingBuilder.bind(queue()).to(delayExchange()).with("test_queue_1").noargs();
  }
}

这里要特别注意的是,使用的是 CustomExchange ,不是 DirectExchange ,另外 CustomExchange 的类型必须是 x-delayed-message 。

实现消息发送

package com.mq.rabbitmq;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;

@Service
public class MessageServiceImpl {

  @Autowired
  private RabbitTemplate rabbitTemplate;

  public void sendMsg(String queueName,String msg) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println("消息发送时间:"+sdf.format(new Date()));
    rabbitTemplate.convertAndSend("test_exchange", queueName, msg, new MessagePostProcessor() {
      @Override
      public Message postProcessMessage(Message message) throws AmqpException {
        message.getMessageProperties().setHeader("x-delay",3000);
        return message;
      }
    });
  }
}

注意在发送的时候,必须加上一个header

x-delay

在这里我设置的延迟时间是3秒。

消息消费者

package com.mq.rabbitmq;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class MessageReceiver {

  @RabbitListener(queues = "test_queue_1")
  public void receive(String msg) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println("消息接收时间:"+sdf.format(new Date()));
    System.out.println("接收到的消息:"+msg);
  }
}

运行Spring Boot程序和发送消息

直接在main方法里运行Spring Boot程序,Spring Boot会自动解析 MessageReceiver 类的。

接下来只需要用Junit运行一下发送消息的接口即可。

package com.mq.rabbitmq;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqApplicationTests {
  @Autowired
  private MessageServiceImpl messageService;
  @Test
  public void send() {
    messageService.sendMsg("test_queue_1","hello i am delay msg");
  }
}

运行完后,可以看到如下信息:

消息发送时间:2018-05-03 12:44:53
3秒钟后,Spring Boot控制台会输出:
消息接收时间:2018-05-03 12:44:56
接收到的消息:hello i am delay msg

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Spring Boot中使用RabbitMQ的示例代码
  • spring boot使用RabbitMQ实现topic 主题
  • spring boot中使用RabbitMQ routing路由详解
  • SpringBoot与rabbitmq的结合的示例
  • Spring Boot整合RabbitMQ开发实战详解
  • 浅谈spring-boot-rabbitmq动态管理的方法
  • springboot集成rabbitMQ之对象传输的方法
  • springboot整合rabbitmq的示例代码
  • Spring Boot与RabbitMQ结合实现延迟队列的示例
(0)

相关推荐

  • spring boot中使用RabbitMQ routing路由详解

    在上一个教程中我们创建了一个扇形(fanout)交换器.我们能把消息已广播的形式传递给多个消费者. 要做什么?Routing 路由 在这个教程中,添加一个新的特性,我们可以只订阅消息的一部分.例如,将只连接我们感兴趣的颜色("orange", "black", "green"),并且把消息全部打印在控制台上. 绑定 交换器和队列是一种绑定关系.简单的理解为:队列对来自这个交换器中的信息感兴趣. 绑定可以加上一个额外的参数routingKey.Sp

  • SpringBoot与rabbitmq的结合的示例

    消息中间件对于我们系统之间的解耦合,消峰等都有极大的帮助.spring boot 也集成了此部分的内容,集成最为容易的是rabbitmq.今天我们就以rabbitmq为例说明. 老规矩,先看下pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <

  • springboot集成rabbitMQ之对象传输的方法

    rabbitMQ的安装方法网上有很多教程,这里就不重复了. 在springboot上使用rabbitMQ传输字符串和对象,本文所给出的例子是在两个不同的项目之间进行对象和和字符串的传输. rabbitMQ的依赖(在两个项目中一样的配置): <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId>

  • Spring Boot中使用RabbitMQ的示例代码

    很久没有写Spring Boot的内容了,正好最近在写Spring Cloud Bus的内容,因为内容会有一些相关性,所以先补一篇关于AMQP的整合. Message Broker与AMQP简介 Message Broker是一种消息验证.传输.路由的架构模式,其设计目标主要应用于下面这些场景: 消息路由到一个或多个目的地 消息转化为其他的表现方式 执行消息的聚集.消息的分解,并将结果发送到他们的目的地,然后重新组合相应返回给消息用户 调用Web服务来检索数据 响应事件或错误 使用发布-订阅模式

  • Spring Boot整合RabbitMQ开发实战详解

    这篇文章主要讲基本的整合.先把代码跑起来,再说什么高级特性. RabbitMQ 中的一些术语 如果你打开 RabbitMQ web 控制台,你会发现其中有一个 Exhanges 不好理解.下面简单说明一下. 交换器(Exchange) 交换器就像路由器,我们先是把消息发到交换器,然后交换器再根据路由键(routingKey)把消息投递到对应的队列.(明白这个概念很重要,后面的代码里面充分体现了这一点) 队列(Queue) 队列很好理解,就不用解释了. 绑定(Binding) 交换器怎么知道把这条

  • spring boot使用RabbitMQ实现topic 主题

    前一篇我们实现了消息系统的灵活配置.代替了使用扇形(fanout)交换器的配置.使用直连(direct)交换器,并且基于路由键后可以有选择性接收消息的能力. 虽然使用直连交换器可以改善我们的系统,但是它仍有局限性,它不能实现多重条件的路由. 在我们的消息系统中,我们不仅想要订阅基于路由键的队列,还想订阅基于生产消息的源.这些概念来自于Unix工具syslog.该日志基于严格的(info/warn/crit...) 和容易的(auth/cron/kern...)的路由方式.我们的例子比这个要简单.

  • Spring Boot与RabbitMQ结合实现延迟队列的示例

    背景 何为延迟队列? 顾名思义,延迟队列就是进入该队列的消息会被延迟消费的队列.而一般的队列,消息一旦入队了之后就会被消费者马上消费. 场景一:在订单系统中,一个用户下单之后通常有30分钟的时间进行支付,如果30分钟之内没有支付成功,那么这个订单将进行一场处理.这是就可以使用延时队列将订单信息发送到延时队列. 场景二:用户希望通过手机远程遥控家里的智能设备在指定的时间进行工作.这时候就可以将用户指令发送到延时队列,当指令设定的时间到了再将指令推送到只能设备. 延迟队列能做什么? 延迟队列多用于需

  • springboot整合rabbitmq的示例代码

    概述 RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据,或者简单地将作业队列以便让分布式服务器进行处理. 它现实了AMQP协议,并且遵循Mozilla Public License开源协议,它支持多种语言,可以方便的和spring集成. 消息队列使用消息将应用程序连接起来,这些消息通过像RabbitMQ这样的消息代理服务器在应用程序之间路由. 基本概念 Broker 用来处理数据的消息队列服务器实体 vhost 由RabbitMQ服务器创建的虚拟消息

  • 浅谈spring-boot-rabbitmq动态管理的方法

    使用spring boot + rabbitmq的时候,在开发过程中,可能会想要临时停用/启用监听,或修改监听消费者数量.如果每次修改都重启比较浪费时间,所以研究了一下不停机就启用停用监听或修改一些配置 一. 关于rabbitmq监听的配置 配置属性类:RabbitProperties,包含rabbitmq的认证.监听.发送者以及其他的一些配置 自动配置类:RabbitAutoConfiguration,主要配置rabbitmq的连接工厂和发送者等,不包含监听的配置 rabbitmq监听的配置是

  • Spring Boot RabbitMQ 延迟消息实现完整版示例

    概述 曾经去网易面试的时候,面试官问了我一个问题,说 下完订单后,如果用户未支付,需要取消订单,可以怎么做 我当时的回答是,用定时任务扫描DB表即可.面试官不是很满意,提出: 用定时任务无法做到准实时通知,有没有其他办法? 我当时的回答是: 可以用队列,订单下完后,发送一个消息到队列里,并指定过期时间,时间一到,执行回调接口. 面试官听完后,就不再问了.其实我当时的思路是对的,只不过讲的不是很专业而已.专业说法是利用 延迟消息 . 其实用定时任务,确实有点问题,原本业务系统希望10分钟后,如果订

  • Spring Boot+RabbitMQ 通过fanout模式实现消息接收功能(支持消费者多实例部署)

    本文章适用的场景:同一条消息可以被多个消费者同时消费.注意:当消费者多实例部署时,会轮询消费消息.网上有大量的的案例展示:P生产一条消息,消费者服务C中建立Q1和Q2两个队列共同消费.但极少的材料展示:P生产一条消息后M1,消费者C1和C2可以同时消费M1,如下图所示.案例基于Spring Boot以及RabbitMQ的“fanout”类型exchange.已经实测可放心使用. 1.引入基本依赖,项目不同请您按自己的情况引入合适的依赖 <dependency> <groupId>o

  • Spring boot+redis实现消息发布与订阅的代码

    一.创建spring boot项目 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId>

  • Spring boot 整合KAFKA消息队列的示例

    这里使用 spring-kafka 依赖和 KafkaTemplate 对象来操作 Kafka 服务. 一.添加依赖和添加配置项 1.1.在 Pom 文件中添加依赖 <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> 1.2.添加配置项 spring: kafka: b

  • Spring Boot集成ShedLock分布式定时任务的实现示例

    一.ShedLock是什么? 官方地址:github.com/lukas-kreca- 以下是ShedLock锁提供者,通过外部存储实现锁,由下图可知外部存储集成的库还是很丰富的 本篇教程我们基于JdbcTemplate存储为例来使用ShedLock锁. 二.落地实现 1.1 引入依赖包 shedlock所需依赖包: <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>she

  • Spring Boot与Spark、Cassandra系统集成开发示例

    本文演示以Spark作为分析引擎,Cassandra作为数据存储,而使用Spring Boot来开发驱动程序的示例. 1.前置条件 安装Spark(本文使用Spark-1.5.1,如安装目录为/opt/spark) 安装Cassandra(3.0+) 创建keyspace CREATE KEYSPACE hfcb WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 3 }; 创建table CREATE

  • spring boot使用WebClient调用HTTP服务代码示例

    这篇文章主要介绍了spring boot使用WebClient调用HTTP服务代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 WebClient的请求模式属于异步非阻塞,能够以少量固定的线程处理高并发的HTTP请求 WebClient是Spring WebFlux模块提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具,从Spring5.0开始提供 在Spring Boot应用中 1.添加Spring WebFlux依赖 <d

  • 详解Spring Boot 定制HTTP消息转换器

    在构建RESTful数据服务过程中,我们定义了controller.repositories,并用一些注解修饰它们,但是到现在为止我们还没执行过对象的转换--将java实体对象转换成HTTP的数据输出流.Spring Boot底层通过HttpMessageConverters依靠Jackson库将Java实体类输出为JSON格式.当有多个转换器可用时,根据消息对象类型和需要的内容类型选择最适合的转换器使用. 在SpringMVC源码剖析之消息转换器HttpMessageConverter一文中,

  • spring boot 部署为jar包的方法示例

    前言 一直在ide中敲代码,使用命令行 mvn spring-boot:run 或者 gradlew bootRun 来运行spring boot项目.想来放到prod上面也应该很简单.然而今天试了下,各种问题.最大错误是1.4的bug: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jp

  • Spring Boot如何优化内嵌的Tomcat示例详解

    前言 本文主要给大家介绍了关于Spring Boot优化内嵌Tomcat的相关内容,分享出来供大家参考学习,下面话不多说了,来一看看详细的介绍吧. Spring Boot测试版本 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE&l

随机推荐