系列博客目录
文章目录
25.Redis消息队列-认识消息队列
消息队列(Message Queue),字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色:
- 消息队列:存储和管理消息,也被称为消息代理(Message Broker)
- 生产者:发送消息到消息队列
- 消费者:从消息队列获取消息并处理消息
这个Message Queue就相当于一个快递柜,快递员你把快递放到快递柜中,然后我们作为消费者从快递柜中去取快递。日常生活中,我们简单想一下,可能我们作为消费者不想有快递柜,想让快递员送货上门,但是如果我们在班上,快递到了还要跑回家签收,其实很不合理,所以快递柜的存在对于快递员和我们消费者都是有利的。消息队列可以解除耦合(左边进行秒杀资格判断,右边把订单写入数据库),提高效率。此时可能和阻塞队列差不多,但是消息队列有自己的特点。
消息队列的主要特点包括解耦性、异步通信、可靠性、缓冲和削峰填谷、顺序性、扩展性和高可用性。
解耦性:消息队列实现了发送方和接收方的解耦,发送方只需将消息发送到队列中,不需要直接知道接收方的存在或状态,接收方通过订阅队列来获取消息,而不需要关心消息的发送者。
异步通信:消息队列支持异步通信模式,发送方将消息发送到队列后可以继续执行其他任务,不需要等待接收方的即时响应,这种异步性提高了系统的响应速度和吞吐量。
可靠性:消息队列通常提供可靠性保证机制,确保消息的可靠传递。它们具有持久化机制、消息确认和重试机制,即使在发送者或接收者出现故障的情况下,消息也不会丢失。
缓冲和削峰填谷:消息队列可以作为缓冲区,平衡消息的生产者和消费者之间的速度差异。当消息的生产速度超过消费速度时,消息会在队列中暂时存储,以便消费者能够以自己的速度进行处理,从而平滑处理流量峰值,避免系统过载。
顺序性:消息队列通常能够保证消息的顺序性。即使发送者并发发送多个消息,接收者也能按照发送的顺序接收到消息,确保消息的一致性和正确性。
扩展性:消息队列具有良好的扩展性,可以根据系统的需求增加或减少消息的发送者和接收者。通过增加消息队列的实例或增加处理消息的消费者,系统可以轻松地扩展以应对高并发和大数据量的情况。
高可用性:消息队列通常具有高可靠性,能够在发送者和接收者之间提供可靠的消息传递。即使在网络故障或系统故障的情况下,消息队列仍能够确保消息的送达。
市面上有好多消息队列,但其他的需要单独搭建环境,不如直接用Redis提供的消息队列。
Redis提供了三种不同的方式来实现消息队列:
- list结构:基于List结构模拟消息队列
- Pubsub:基本的点对点消息模型
- Stream:比较完善的消息队列模型
26.Redis消息队列-基于List实现消息队列
比如你开启两个窗口,一个使用BRPOP,如果消息队列中没有数据,会阻塞住,另外一个窗口如果输入进出数据了,卡住的窗口会解除阻塞,输出被输入的数据。
27.Redis消息队列-PubSub实现消息队列
不支持持久化:发完了没人收,相当于直接丢弃,消息在传递之后通常会被丢弃。
无法避免消息丢失:如果消费者未能及时处理消息,或者Pub/Sub系统发生故障,消息可能会丢失。对于不容忍丢失的场景,通常需要结合消息队列(如RabbitMQ或Kafka)来进行持久化或增加重试机制。
消息堆积有上限:Pub/Sub系统通常有缓存限制,消息堆积到一定数量后可能会丢失,尤其是当消费者处理速度无法跟上消息生产速度时。为了避免这种情况,可以使用流控、分区机制或者动态扩容策略来优化消息的处理能力。
有没有更好的消息队列呢,下面介绍。
28.Redis消息队列-Stream的单消费模式
基于Stream的消息队列
如下图所示:一个消息可以被读多次。
如下图所示,被读过的,不能再用$号读。还可以设置阻塞读取。
通过XREAD,同一个消息可以被读多次。被读过的不算是“最新消息”。
STREAM类型消息队列的XREAD命令特点:
- 消息可回溯
- 一个消息可以被多个消费者读取
- 可以阻塞读取
- 有消息漏读的风险
29.Redis消息队列-Stream的消费者组模式
基于Stream的消息队列-消费者组
消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。具备下列特点:
- 01 消息分流:队列中的消息会分流给组内的不同消费者(这些消费者竞争的抢 消息),而不是重复消费,从而加快消息处理的速度。
- 02 消息标示:消费者组会维护一个标示记录最后一个被处理的消息哪怕消费者宕机重启,还会从标示之后读取消息。确保每一个消息都会被消费。
- 03 消息确认:消费者获取消息后,消息处于pending状态(待处理状态),并存入一个pending-list。当处理完成后需要通过XACK来确认消息,标记消息为已处理,才会从pending)list移除。
如下图所示,一个“>” 消费一个消息。
如下图所示,通过XACK来确认消息。
返回Pendinglist中的消息列表。
下图展示了如何获取Pendinglist中的消息,获取之后可以进行确认。
下面的图是流程图,后面会用到
基于Redis的Stream的消息队列,对于生产者这部分出现问题是没法解决的,中小型企业可以使用,如果大型企业还是要用专业的消息队列。
30.Redis消息队列-基于Stream消息队列实现异步秒杀
第一个需求,在创建队列的时候,不需要使用JAVA代码,因为一次创建,可以一直使用。
针对第二个需求编写LUA脚本。注意这里我们与之前有所不同,之前的LUA只需要判断判断资格,再由JAVA代码往阻塞队列中写数据。现在写数据也由LUA脚本完成。少了一次JAVA和LUA的交互,效率更高。
-- 1.参数列表
-- 1.1.优惠券id
local voucherId = ARGV[1]
-- 1.2.用户id
local userId = ARGV[2]
-- 1.3.订单id
local orderId = ARGV[3]
-- 2.数据key
-- 2.1.库存key
local stockKey = 'seckill:stock:' .. voucherId
-- 2.2.订单key
local orderKey = 'seckill:order:' .. voucherId
-- 3.脚本业务
-- 3.1.判断库存是否充足 get stockKey
if(tonumber(redis.call('get', stockKey)) <= 0) then
-- 3.2.库存不足,返回1
return 1
end
-- 3.2.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call('sismember', orderKey, userId) == 1) then
-- 3.3.存在,说明是重复下单,返回2
return