本文同步Java知音社区,专注于Java

作者:Sam哥哥http://blog.csdn.net/linsongbin1/article/details/80178122

概述

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

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

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

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

我当时的回答是:

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

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

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

目前网上关于使用RabbitMQ实现延迟消息的文章,大多都是讲如何利用RabbitMQ的死信队列来实现,实现方案看起来都很繁琐复杂,并且还是使用原始的RabbitMQ Client API来实现的,更加显得啰嗦。更多springboot整合实战内容,可以在Java知音公众号回复“springboot内容聚合”

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

软件准备

erlang

请参考Win10下安装erlang

https://blog.csdn.net/linsongbin1/article/details/80170487

本文使用的版本是:

  • Erlang 20.3

RabbitMQ

请参考win10下安装rabbitmq

https://blog.csdn.net/linsongbin1/article/details/80170567

本文使用的是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;@Beanpublic ConnectionFactory connectionFactory() {CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(host,port);cachingConnectionFactory.setUsername(userName);cachingConnectionFactory.setPassword(password);cachingConnectionFactory.setVirtualHost("/");cachingConnectionFactory.setPublisherConfirms(true);return cachingConnectionFactory;}@Beanpublic 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 {@Beanpublic 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);}@Beanpublic Queue queue() {Queue queue = new Queue("test_queue_1", true);return queue;}@Beanpublic 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 {@Autowiredprivate 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() {@Overridepublic 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 {@Autowiredprivate MessageServiceImpl messageService;@Testpublic 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 实现延迟消息实现完整版,实用!相关推荐

  1. mall整合RabbitMQ实现延迟消息

    摘要 本文主要讲解mall整合RabbitMQ实现延迟消息的过程,以发送延迟消息取消超时订单为例.RabbitMQ是一个被广泛使用的开源消息队列.它是轻量级且易于部署的,它能支持多种消息协议.Rabb ...

  2. RabbitMQ实现延迟消息

    本文主要讲解mall整合RabbitMQ实现延迟消息的过程,以发送延迟消息取消超时订单为例. 文章目录 项目使用框架介绍 RabbitMQ RabbitMQ的安装和使用 Lombok 业务场景说明 整 ...

  3. 商城项目(七)整合RabbitMQ实现延迟消息

    商城项目(七)整合RabbitMQ实现延迟消息 本文主要整合RabbitMQ实现延迟消息的过程,以发送延迟消息取消超时订单为例. 环境配置 RabbitMQ RabbitMQ是一个被广泛使用的开源消息 ...

  4. rabbitmq 取消消息_SpringBoot整合RabbitMQ实现延迟消息

    ## RabbitMQ RabbitMQ是一个被广泛使用的开源消息队列.它是轻量级且易于部署的,它能支持多种消息协议.RabbitMQ可以部署在分布式和联合配置中,以满足高规模.高可用性的需求. R ...

  5. java B2B2C 仿淘宝电子商城系统-基于Rabbitmq实现延迟消息

    预备知识 1.1 消息传递 首先我们知道消费者是从队列中获取消息的,那么消息是如何到达队列的? 当我们发送一条消息时,首先会发给交换器(exchange),交换器根据规则(路由键:routing ke ...

  6. 【Spring Boot】Spring Boot之整合RabbitMQ并实现消息的发送和接收

    一.项目配置 1)引入maven坐标 <!--amqp--><dependency><groupId>org.springframework.boot</gr ...

  7. RabbitMQ 延迟消息的极限是多少?

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 之前在写Spring Cloud Stream专题内容的时候,特地介绍了一下如何使用Rabb ...

  8. RabbitMQ延迟消息场景分析以及实现两种方式(SpringBoot)

    使用场景,不限于下面 用户下订单结束,如果用户未及时支付,后续需要取消订单,可以怎么做?定时任务可以做,但是不能接近实时或者消耗数据库性能太大. [数据库定时任务方案]:定时任务可以做到订单的状态的改 ...

  9. RabbitMQ延迟消息的极限是多少?

    之前在写Spring Cloud Stream专题内容的时候,特地介绍了一下如何使用RabbitMQ的延迟消息来实现定时任务.最近正好因为开发碰到了使用过程中发现,延迟消息没有效果,消息直接就被消费了 ...

最新文章

  1. TIOBE 5 月编程语言排行榜:Python、C++竞争白热化,Objective-C已沦为小众语言
  2. IOS-开发日志-UILabel相关
  3. 重磅!2021 高通人工智能应用创新大赛今日开赛
  4. C++string容器-字符串拼接
  5. dubbo-admin管理平台搭建
  6. 2020年中国新世代用户视频消费行为洞察
  7. 【LeetCode】剑指 Offer 20. 表示数值的字符串
  8. 计算机 运行新ie 命令,电脑中通过执行命令来清除IE浏览器隐私数据的方法
  9. LeetCode Week 1:第 1 ~ 10 题
  10. Python:获取文件夹内 文件夹 和 文件数量
  11. SpringBoot --thymeleaf(资源文件css、js的引入)
  12. (转)金融从业人员的核心竞争力在哪里?
  13. matlab时域采样理论得验证,基于matlab时域采样和频域采样验证毕业设计
  14. 弹性地基梁板实用计算_YJK软件前处理之计算参数的设置(上篇)
  15. 高德api地图的调用
  16. vue遍历中key详解 (Demo案例)
  17. 漫谈OCL概念、特征和实践(作者:大雁北飞)
  18. SEVERE: Error configuring application listener of
  19. Tarjan算法 —— 强连通双连通缩点 模板
  20. 查找文献必备的文献下载神器

热门文章

  1. java与java ee_计划Java EE 7批处理作业
  2. JMetro版本11.6.5和8.6.5发布
  3. 计算机图形学论文_论图计算
  4. 初学者指南:如何为Red Hat Process Automation Manager配置电子邮件服务任务
  5. JEP 181不兼容,嵌套类/ 2
  6. 处理JAX-RS中的自定义异常类型
  7. Java命令行界面(第23部分):Rop
  8. 将CloudWatch Logs与Cloudhub Mule集成
  9. maven jpa_使用Hibernate 4,JPA和Maven的架构创建脚本
  10. 一时冲动:“通往瓦尔哈拉之路的冒险”