最近业务中有有这样一个场景,就是用户在商城下单之后,如果30分钟没有付款,那么就需要将这个订单处理掉,要么直接删除,要么直接标识为失效状态,为什么要这么做?
- 1、库存,用户在下单之后,会锁定一个库存,如果用户一直不支付,那么就会占用库存,影响别的用户购买,
- 2、随着业务的发展,用户量的增加,我们的订单数据会越来越多,那么我们要及时的清理无效的订单,提升系统的性能;
曾经的纯洁无瑕
首先说下,我曾经那些纯洁无瑕的想法,第一次看到这种需求的时候,如果要清理失效的订单,那我直接写一个定时任务,5分钟或者10分钟跑一次,删除过期的订单,增加库存。
定时任务确实可以解决上面的问题,但是存在一个现实的问题,那就是数据库压力,甚至影响整个系统的性能,如果你是几百、几千个订单还好,要是十几万、甚至上百万,那么此方法肯定是行不通的。
进阶版本
既然发现了上面的方法不行,那么就重新想办法,有什么办法可以不用查询数据库,就可以知道哪些订单快要过期,再这样的思考下,我用Redis做了一个消息队列,当生成订单的时候,生成一个延迟10分钟消息,10分钟结束的时候,就会从Redis的队列中将这个订单取出来,然后这样我就可以将这个订单删除,增加库存。
Redis做消息队列存在的问题
之前用一个全新的Redis做消息队列,存储的数据也不多,感觉还好,当随着业务增加,Redis存储超过90%的时候,大量的消息没有被消费,就是消息丢失很严重。那么这样肯定是不行的。
删除订单,增加库存这是不能有太多误差的事情,所以Redis消息队列已经不能满足我的需求,那么就需要可靠性高的消息队列,也就是我们这次要介绍的RabbitMQ。
RabbitMQ安装与面板介绍
这里我就不跟大家介绍如何安装RabbitMQ了,网上其实有很多这种教程,所以大家自行搜索吧。重点要跟大家说下,RabbitMQ的面板,我们的消息队列,以及消息都是可以在面板上看到的。我是用的MQ的版本是3.8,各个版本之间的面板多多少少可能有点不太一样。
第一次接触的话,我们不要想着全部我一次性都看懂,都知道是干嘛的,我觉得没必要,先熟悉最基础的,我在上面圈了两个地方Queue、Admin和Add a new queue 这个三个是最基本的,我们学习必须要用的东西。
- Queue:这个就是我们声明的消息队列;
- Admin:用户管理,RabbitMQ默认有一个用户是guest,但是RabbitMQ神奇的就是每个库都必须创建一个用户角色;
- Add a new queue:这个就是创建一个新的队列,但是我们一般不这么直接创建,而是在代码中创建;
再来补充下Admin吧,首先要告诉大家一个基本的东西,就是如果想要声明一个队列,那么你必须要有一个库(比喻手法),队列存在于库中,可以想象下mysql,是不是得先有库,再有表。MQ也是这样的。
右边的Virtual Hosts就是创建库,Name就是库名,写的时候前面必须加/;
如果细心的朋友可以看到第一张图中两个队列前面就是库名,标识队列存在与xiaoshuo库中。
一个简单的消息队列
当生产者生产出消息之后,发送到队列中,消费者监听到队列中有消息进行消费,那么我们本篇就先实现一个简单的消息队列。
代码
我们不使用SpringBoot框架,我们就从基本的写,人生原生的API,这样以后才懂得什么是怎么回事。
1、引入需要的pom文件
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
2、连接RabbitMQ
/**
* @description: TODO MQ连接工厂
* @author: bingfeng
* @create: 2020-05-07 08:55
*/
public class MQConnectUtil {
public static Connection getConnection() throws IOException, TimeoutException {
// 定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 设置连接地址
factory.setHost("127.0.0.1");
// 设置端口
factory.setPort(5672);
// 选择 vhost
factory.setVirtualHost("/xiaoshuo");
// 设置用户名
factory.setUsername("bingfeng");
// 密码
factory.setPassword("123");
return factory.newConnection();
}
}
3、发送消息
/**
* @description: TODO 模拟发送消息
* @author: bingfeng
* @create: 2020-05-07 09:01
*/
public class Producer {
/**
* 队列名
*/
public static final String QUEUE_NAME = "simple_queue_test";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = MQConnectUtil.getConnection();
// 从连接中获取一个通道
Channel channel = connection.createChannel();
// 创建队列声明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 消息内容
String msg = "你好,冰峰!";
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
System.out.println("发送消息:" + msg);
channel.close();
connection.close();
}
}
4、消费消息
/**
* @description: TODO 消费MQ
* @author: bingfeng
* @create: 2020-05-07 09:11
*/
public class Consumer {
public static final String QUEUE_NAME = "simple_queue_test";
public static void main(String[] args) throws Exception {
// 获取连接
Connection connection = MQConnectUtil.getConnection();
// 创建频道
Channel channel = connection.createChannel();
// 队列声明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 定义消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
String msg = new String(body, StandardCharsets.UTF_8);
System.out.println("消费消息:" + msg);
}
};
// 监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
当我们发送一个消息之后,我们可以直接在面板看到队列中的消息数
点进来之后,拉到下面,Messages就是我们想要查看的消息数量,就是你想看几条消息填几就行了,填完之后点击下面的Get Messages,我们最近的消息就会显示在下面。
当我们消费了这个消息之后,队列中就没有这个消息了。
最后的话
今天就写到这,我打算把RabbitMQ从入门到项目中的实战用法全部一级一级分享给大家,一是防止自己以后忘记可以回来翻翻看,二是分享给大家有兴趣的朋友可以一起学习,后面我也会把我之前说的,订单过期删除的业务场景也会写一遍。
熟悉RabbitMQ的朋友,可能看下来解决写的东西很简单,但是毕竟也有很多人实际工作中并没有用过MQ,自己可能也没有了解过,对于没有了解过的朋友来说,我觉得入个门还是挺不错的。
有什么问题,欢迎大家下方solo。