消息中间件的使用场景,流行的消息中间件有哪些?
- Redis
- 2023-03-16
- 1057热度
- 0评论
应用场景
1.业务场景
对于一个电商APP而言,每卖掉了一个商品,就要扣减掉商品的库存,而且一旦用户成功支付了,还需要将订单的状态更新成待发货。
在完成这些最核心的功能后,其实是有很多事情要做的,如果这些动作都以同步方式来完成,根据线上系统的一般统计,多个子步骤全部执行完毕,加起来大概需要1秒~2秒的时间。
有时候在高峰期并发量特别大,服务器的磁盘、IO、CPU的负载会很高,执行SQL语句的性能也会有所下降。因此有的时候甚至需要几秒钟的时间完成上述几个步骤。
想象一下,如果你是一个用户,在支付完一个订单之后,界面上会有一个圈圈不停的旋转,让你等待好几秒之后才能提示支付成功。对用户来说几秒钟的时间,会让人非常不耐烦的!
2.解决问题
所以首先针对子步骤过多、速度过慢、让用户支付之后等待时间过长的问题,就是订单系统急需解决的问题!
而解决这个问题的一大利器就是消息中间件,英文全称“Message Queue”,简称MQ。
在引入消息中间件以后,系统A和系统B之间就由同步变为异步通信,而完成这样的一个核心概念就是“消息”。
系统A发送消息给MQ后,就认为已经完成了自己的任务;然后系统B根据自己的情况,可能会在系统A投递消息到MQ之后的1秒内,也可能是1分钟之后,也可能是1小时之后,多长时间都有可能。
反正不管是多长时间后,系统B会根据自己的节奏从MQ里获取到一条属于自己的消息,再根据消息的指示完成自己的工作。
在“异步调用”的整个过程中,系统A仅仅是发个消息到MQ,至于系统B什么时候获取消息,有没有获取消息,系统A是不管的。
消息队列
当我们在使用一个消息队列时,希望它的功能如下:
- 支持阻塞等待拉取消息
- 支持发布 / 订阅模式
- 消费失败,可重新消费,消息不丢失
- 实例宕机,消息不丢失,数据可持久化
- 消息可堆积
顺着这个思路,再重新思考一下订单系统。由于支付订单流程中,过多的子步骤如红包发放、短信push通知、积分等等导致性能很差。
在引入MQ后,可以让订单系统仅仅完成最核心的功能,然后发送消息到MQ。比如需要进行减库存,就发送一个消息到库存消息队列中,然后库存系统从这个MQ里获取消息再进行处理就可以,把这些很耗时的步骤慢慢执行,从而也实现了系统之间的解耦。
在大促活动的时候,同样可以让瞬间涌入的大量下单请求到MQ里去排队,然后让订单系统在后台慢慢的获取订单,以数据库可以接受的速率完成操作,避免瞬间请求量过大击垮数据库。
这也是MQ的另一重要功能:削峰填谷。
所谓消息中间件,就是一种系统,它自己本身也是独立部署的,通过消息的收发,是多个系统之间不局限于同步调用,通过异步调用更好地实现解耦。
目前业界使用最广泛的是Kafka、RabbitMQ以及RocketMQ这三种消息中间件。
Redis消息队列
Redis 很轻量,自带好几种可以用来实现消息队列的功能,用来作队列也很方便。
- list链表
- 订阅/发布
- Redis Stream
1.pub/sub优缺点
Pub/Sub 的优缺点:
- 支持发布 / 订阅,支持多组生产者、消费者处理消息
- 消费者下线,数据会丢失
- 不支持数据持久化,Redis 宕机,数据也会丢失
- 消息堆积,缓冲区溢出,消费者会被强制踢下线,数据也会丢失
Pub/Sub缓冲区是有「上限」的(可配置),如果消费者拉取消息很慢,就会造成生产者发布到缓冲区的消息开始积压,缓冲区内存持续增长。
如果超过了缓冲区配置的上限,此时,Redis 就会「强制」把这个消费者踢下线。
这时消费者就会消费失败,也会丢失数据。
如果你有看过 Redis 的配置文件,可以看到这个缓冲区的默认配置:client-output-buffer-limit pubsub 32mb 8mb 60。
- 32mb:缓冲区一旦超过 32MB,Redis 直接强制把消费者踢下线
- 8mb + 60:缓冲区超过 8MB,并且持续 60 秒,Redis 也会把消费者踢下线
2.stream
Stream 通过 XADD 和 XREAD 完成最简单的生产、消费模型:
- XADD:发布消息
- XREAD:读取消息
Stream 通过以下命令完成发布订阅:
- XGROUP:创建消费者组
- XREADGROUP:在指定消费组下,开启消费者拉取消息
Stream 保证消息不丢失,重新消费的方式:
当一组消费者处理完消息后,需要执行 XACK 命令告知 Redis,这时 Redis 就会把这条消息标记为「处理完成」。
// group1下的 1618472043089-0 消息已处理完成
127.0.0.1:6379> XACK queue group1 1618472043089-0
如果消费者异常宕机,肯定不会发送 XACK,那么 Redis 就会依旧保留这条消息。
待这组消费者重新上线后,Redis 就会把之前没有处理成功的数据,重新发给这个消费者。这样一来,即使消费者异常,也不会丢失数据了。
Stream处理消息堆积:
在发布消息时,可以指定队列的最大长度,防止队列积压导致内存爆炸。
// 队列长度最大10000
127.0.0.1:6379> XADD queue MAXLEN 10000 * name zhangsan
"1618473015018-0"
Kafka
1.优势
- Kafka的吞吐量几乎是行业里最优秀的,在常规的机器配置下,一台机器可以达到每秒十几万的QPS,可谓是消息中间件中的顶尖水平。
- 性能高,发送消息给Kafka可以控制在毫秒级。
- 可用性高,Kafka能够支持集群部署,如果部分服务器发生了宕机,集群其余部分可以保证任务的继续运行。
2.劣势
- Kafka的存储策略是在消息收到之后,将其写入磁盘缓冲区内,并不会直接存储到物理硬盘上。这就导致了假如机器本身发生故障,磁盘缓冲区里的数据非常有可能丢失。而消息作为最重要的资源, kafka的这一特点有可能造成严重后果。
- Kafka另外一个比较大的缺点是功能比较单一。它使用了典型的推拉架构设计,生产者将发送消息给它,然后消费者在从它拉取消息进行消费。除此之外,其他就没有什么额外的高级功能,所以基于Kafka有限的功能,一些比较复杂的商业场景并不是很适合。
RabbitMQ
再说RabbitMQ,在RocketMQ出现之前,国内大部分公司,包括很多一线互联网大厂都在使用RabbitMQ,而且直到目前,还有很多中小型公司在使用RabbitMQ。
1.优势
- 可以有保证数据不丢失的机制。
- 保证高可用性,集群部署的时候部分,即使部分服务器宕机可以继续执行任务。
- 实现了部分高级功能,比如死信队列,消息重试。
2.劣势
- RabbitMQ的吞吐量是比较低,只有每秒几万的级别,遇到像双十一这样的高并发场景,很容易到达性能的瓶颈。
- 维护比较复杂,在集群部署时,如果需要线性扩展,比较麻烦。
- 它的开发语言是erlang,国内目前大大小小公司的技术骨干大部分都是BAT背景,精通erlang语言的还是少数。阅读源代码非常困难,也就更加无从谈起根据个性化要求修改源代码了
RocketMQ
RocketMQ是阿里开源的消息中间件,经过实战的检验,比较靠谱。后发优势使它在最初设计的时候,就为了去解决Kafka和RabbitMQ所存在的缺陷。
1.优势
- 性能强大,吞吐量高,能够达到10万QPS以上的级别
- 可以保证高可用性,能够大规模集群化部署
- 支持通过配置保证数据绝对不丢失
- 满足多种需求,比如说延迟消息、事务消息、消息回溯、死信队列、消息积压等等。
- RocketMQ是基于Java开发的,符合国内大多数公司的技术栈,很容易就可以阅读他的源码,甚至是修改他的源码。
2.劣势
- 当然RocketMQ也有美中不足的地方,RocketMQ的官方文档相对简单,而这也是国内开源软件和社区的一个通病,可能国内996的工作强度导致大家没有更多的精力维护。