使用RabbitMQ打造扛得住的高并发环境(三)
1. 前言
Hello,大家好。我们在上一小节中,介绍了使用 RabbitMQ 打造扛得住的高并发环境系列内容的第二小节部分,也就是我们此系列小节内容的基础核心内容。
本小节会继续介绍使用 RabbitMQ 打造扛得住的高并发环境系列内容的第三部分,在本小节中,我们会使用我们在第二节中使用 RabbitMQ 和 Redis 打造的高可用消息队列去实现我们的一个真实的高并发的业务场景。
本小节会首先介绍一种老师在自己的实际工作中,出现的真实的高并发业务场景,并且,老师在应对这种高并发业务场景时,采用的基本解决方案就是采用的 RabbitMQ 和 Redis 共同改造的高可用消息队列这种形式,还在等什么,让我们先睹为快吧!
本节主要内容:
-
实际高并发业务场景概述;
-
实际高并发业务场景实现;
-
实际高并发业务场景测试。
2.实际高并发业务场景概述
本部分内容,老师会详细介绍本套课程最终需要实现的一种业务场景,此种业务场景是老师自己在实际工作中遇到的真实的业务场景,同学们一定要先对这种业务场景有个清晰地了解之后,在继续学习本小节后续地内容,如果你对这种业务场景没有充分地了解,那么后面的实现思路你将不会看懂,这点同学们注意。
本业务场景实际上并不算复杂,我们每个同学在真实的日常生活中,或多或少都会接触到,只不过平时同学们可能不会注意观察或者思考。这种业务场景有一个专有的代名词,相信大家都已经听说过了,那就是’秒杀’业务场景。
那么,什么是秒杀业务场景呢?
这个秒杀的业务场景,出现在销售行业的居多,比如日常生活中,我们在超市中去购买一种商品,这种商品的价格要比往常的价格要低很多,但是,这种商品的库存数量是有限的,当我们购买这种商品时,必须要在一瞬间完成抢购这一动作。
随着互联网时代的快速发展,越来越多的线上电子商城已经出现在人们的日常生活中,以淘宝、京东为代表性的互联网电子商城巨头率先将这些线下的商品购买行为,转换为线上的商品购买功能。
针对与上述这种秒杀抢购的业务场景,目前在各互联网电子商城巨头中都是有所体现的,比如我们熟知的双十一活动,以及 618 商品大促活动,这些都是秒杀抢购业务场景的典型代表,那么,这种业务场景在线上又是如何实现的呢?
在分析一秒杀抢购业务场景的一个完整的线上业务流程是什么样的之前,我们先来看一下,一般地线上商品购买的一个完整的业务流程是什么样的,如下图所示:
首先,用户在有这种秒杀抢购的实际需求之后,用户首先会登录我们的线上商城系统,在用户成功登录本系统之后,用户需要到我们线上商城系统的秒杀抢购专区,用户可以在这个秒杀抢购专区中看到本商城系统中参与秒杀抢购活动的商品,这一过程我们称为用户挑选商品阶段。
用户在挑选完自己所需要的商品之后,可以将所需的商品放入购物车中,也可以直接点击下单按钮,来迅速完成对某一具体商品的下单操作。
如果用户是将商品放入购物车中,那么用户只能进行一个批量下单的动作,即用户前往自己的购物车中,选中商品之后,点击下单按钮,进行一个批量下单操作,这一过程我们称为用户预下单阶段。
在用户将订单创建完毕之后,就需要用户选择对应的支付方式,来完成商品价格的支付动作,对于线上电子商城而言,用户可以选择不同厂家的扫码支付功能来完成支付,这一过程我们称为用户支付阶段。
在用户对所购商品支付完成之后,我们需要将用户的商品支付结果返回给用户,告知用户商品支付的状态,是支付成功了,还是支付过程中遇到问题,导致支付失败了,这一过程往往我们会采取轮询的方式实现,这一过程我们称为用户支付状态回调阶段。
在用户的支付状态成功回调给用户之后,一个完整的线上商品购买流程就结束了,至于后续地商品物流信息等其他信息就不属于我们商品购买的流程了。
在清楚了一般地商品购买全流程之后,我们就不难理解秒杀抢购的业务场景流程了。
其实,秒杀抢购的业务场景流程和一般地商品购买流程是一模一样地,只不过在用户预下单阶段,以及用户支付阶段,在同一时刻会有大量的用户请求需要我们处理,这就是秒杀业务场景和一般地商品购买流程中最大的区别点,其他地方并没有什么区别。
在本小节中,我们需要实现上述业务场景,并对核心的秒杀抢购业务场景中的用户预下单阶段,以及用户支付阶段,做好高并发场景下的处理。
Tips: 同学们一定要清楚地理解上述所介绍的业务流程,如果看一遍不理解,那就反复多看几遍,直到自己理解了即可。
3.实际高并发业务场景实现
在了解了秒杀抢购的业务场景流程之后,接下来我们就需要实现这一业务场景了,那么,这种业务场景我们应该怎么用 RabbitMQ 和 Redis 去实现呢?
在使用 RabbitMQ 打造扛得住的高并发环境系列小节内容的第二小节中,我们使用 RabbitMQ 消息通信中间件和 Redis 缓存中间件,对 RabbitMQ 自身的消息队列进行了改造,改造成了一种 Redis 承载的高可用的消息队列,在本节,我们就会用到这一高可用的消息队列。
在实现上述实际高并发业务场景时,由于篇幅原因,我们并不会从用户登录开始,逐步地去实现每一个过程,我们只实现在秒杀抢购业务场景中,最核心的部分,也就是,当我们在秒杀抢购商品区域,点击立即购买这个秒杀按钮时,我们后台所需要应对高并发处理的内容。
让我们来看看具体应该怎么设计实现吧。
3.1 初始化 Redis 缓存数据
当我们点击立即购买这个秒杀按钮时,我们首先会获取到用户所挑选的商品数据, 在获取到这些商品数据之后,我们需要根据这些商品数据中起到唯一区分商品的这一属性,去查询我们对应商品的库存是否充足。
查询库存这种操作,我们放在 Redis 缓存中进行存储。即,当我们的后台服务启动时,或者是在一个其他的什么时机的时候,我们会将系统中参与秒杀抢购的所有商品数据,或者这些关键的商品数据,放入到我们的 Redis 缓存中,这些数据中间就包括商品的库存数量,如下代码所示:
代码实现:
// 向 Redis 缓存中初始化存储秒杀商品数据
redisUtil.set("shipping_seckill" + shipping.getId(), shipping);
代码解释:
我们使用 redisUtil 工具类的 set 方法,将参与秒杀抢购的商品数据 shipping ,在后台服务初始化时,放入到 Redis 缓存中,以备后续使用。
3.2 Redis 预减商品库存
在校验商品库存时,我们会只查询 Redis 缓存中的商品数据, 查看商品库存是否足够支撑用户的商品购买数量,如果库存数量不足以支撑,则提示用户商品库存不足,预下单失败;如果库存数量充足,则提示用户商品预下单成功,如下代码所示:
代码实现:
Shipping shipping = redisUtil.get("shipping_seckill" + shipping.getId());
Integer shippingStorage = shipping.getStorage();
if(shippingStorage >= userCurrNums) {
// 预减库存,并提示用户商品预下单成功
}
代码解释:
第 1 行,我们使用 redisUtil 工具类的 get 方法,来获取到存储于 Redis 缓存中的商品数据。
第 2 行,我们通过声明一个 shippingStorage 变量,来进一步获取到用户所挑选商品的库存信息。
第 3-5 行,我们通过将商品库存 shippingStorage 与用户所购买的商品数量 userCurrNums 做一个比较,来判断当前商品的库存是否充足,并将结果提示给用户。
3.3 RabbitMQ 处理秒杀商品订单
在商品预减库存成功之后,我们需要根据实际的业务需求来生成商品的订单,并将该订单发送到我们的 RabbitMQ 消息队列中去,如下代码所示:
代码实现:
Shipping shipping = redisUtil.get("shipping_seckill" + shipping.getId());
Integer shippingStorage = shipping.getStorage();
if(shippingStorage >= userCurrNums) {
// 预减库存成功,开始处理商品订单
rabbitTemplate.convertAndSend("seckill_order_ex", "seckill_order_key", order, message -> {
// 设置具体的消费配置参数
})
}
代码解释:
第 1-3 行,和上述代码相同,不再赘述。
第 5 行,我们使用 rabbitTemplate 的 convertAndSend 方法,来设置秒杀商品订单所需要的交换机,routing key ,并将秒杀商品订单发送到我们的 RabbitMQ 中。
将秒杀商品订单发送到我们的 RabbitMQ 中之后,我们还需要对消息配置一种监听器,来监听消息有没有真正到达 RabbitMQ 的交换机中。(由于篇幅原因,配置监听部分请参考《RabbitMQ消息确认与返回机制介绍》小节)。
3.4 消费者完成商品支付
配置好监听之后,我们的消费者就需要将消息进行消费了。从 RabbitMQ 消息队列中获取到消息之后,会返回给我们一个消息 ack 或 nack 的应答结果,表明消费者是否成功获取到了消息。
当返回 ack 确认应答结果时,表明消费者已经成功获取到了消息,此时,我们应该根据消费者所获取到的秒杀订单数据,来生成对应场景的支付二维码,以提示用户扫码来完成商品支付。
当用户支付成功之后,我们需要根据用户的支付结果,同步更新我们 Redis 缓存,以及数据库中,用户秒杀商品和秒杀订单的商品库存数据,以及订单的状态, 实现代码如下所示:
代码实现:
switch (order.getPayState()) {
case 0 :
this.updateSeckillOrderInfo(order.getShippingId(), 0);
// 下同
default:
break;
}
代码解释:
第 1 行,我们使用了 switch 语句,来获取用户秒杀订单的支付状态。
第 2-7 行,我们使用 case 语句,来对不同的用户秒杀订单的支付状态做出不同的数据处理,比如,支付状态为 0 时,表示用户支付成功,那么此时我就需要调用 updateSeckillOrderInfo 方法来更新用户秒杀订单的支付状态。
至此,我们已经基本实现了秒杀抢购业务场景中的核心内容,即用户点击立即购买之后,我们的后台服务来处理高并发请求的业务场景。
4.实际高并发业务场景测试
那么,我们虽然基本实现了这一业务场景中的核心部分内容,但是,我们的这个秒杀抢购商品的接口可以同时支撑多大的 QPS 呢?
接下来,让我们使用 JMeter 来做一个简单的测试,来看一下我们采用 RabbitMQ 和 Redis 的方式之下,我们这个服务接口的 QPS 吞吐量是多少,测试结果如下图所示:
Tips:
1. 关于 JMeter 我们不会做任何介绍,需要同同学们课下进行了解。
2. 由于测试环境的不同,使用 JMter 测试的结果也会不进相同,上下存在一定的误差,这是合理的。
3. 关于 QPS 的概念介绍,也需要同学们自行了解,本套课程不会做任何介绍。
4. 测试部分并不是我们课程内容的重点,只不过是为了体现一种量化的指标,来供同学们查看,这点同学们要搞清楚。
5. 小结
本小节为同学们介绍了使用 RabbitMQ 打造扛得住的高并发环境的第三部分内容,其主要介绍了,如何使用 RabbitMQ 和 Redis 共同打造的高可用消息队列,来实现我们真实的一种秒杀抢购的业务场景,希望同学们都能够理解这部分内容。
随着使用 RabbitMQ 打造扛得住的高并发环境系列小节内容的结束,本套课程的进度也接近了尾声。通过对 RabbitMQ 消息通信中间件系统性地介绍,希望同学们可以将 RabbitMQ 真正地应用到我们的项目中去。
最后,感谢各位同学的持续关注,本套课程就到这里了,希望同学们可以持续支持与关注,江湖路远,我们有缘再会!