前言
第一次听到“消息队列”这个词时,不知你是不是和我反应一样,感觉很高阶很厉害的样子,其实当我们了解了消息队列之后,发现它与普通的技术类似,当我们熟悉之后,也能很快地上手并使用。
本文将会深入的讲解 MQ(Message Queue,消息队列)中间件,以及这些热门中间件的具体使用。
MQ 常见的使用场景有哪些?你都用过哪些 MQ 中间件?
典型回答
消息队列的使用场景有很多,最常见的使用场景有以下几个。
1.商品秒杀
比如,我们在做秒杀活动时,会发生短时间内出现爆发式的用户请求,如果不采取相关的措施,会导致服务器忙不过来,响应超时的问题,轻则会导致服务假死,重则会让服务器直接宕机,给用户带来的体验也非常不好。如果这个时候加上了消息队列,服务器接收到用户的所有请求后,先把这些请求全部写入到消息队列中再排队处理,这样就不会导致同时处理多个请求的情况;如果消息队列长度超过可以承载的最大数量,那么我们可以抛弃当前用户的请求,通知前台用户“页面出错啦,请重新刷新”等提示,这样就会有更好的交互体验。
2.系统解耦
使用了消息队列之后,我们可以把系统的业务功能模块化,实现系统的解耦。例如,在没有使用消息队列之前,当前台用户完善了个人信息之后,首先我们需要更新用户的资料,再添加一条用户信息修改日志。但突然有一天产品经理提了一个需求,在前台用户信息更新之后,需要给此用户的增加一定的积分奖励,然后没过几天产品经理又提了一个需求,在前台用户信息更新之后,不但要增加积分奖励,还要增加用户的经验值,但没过几天产品经理的需求又变了,他要求完善资料无需增加用户的积分了,这样反反复复、来来回回的折腾,我想研发的同学一定受不了,但这是互联网公司的常态,那我们有没有一劳永逸的办法呢?
没错,这个时候我们想到了使用消息队列来实现系统的解耦,每个功能的实现独立开,只需要一个订阅或者取消订阅的开关就可以了,当需要增加功能时,只需要打开订阅“用户信息完善”的队列就行,如果过两天不用了,再把订阅的开关关掉就行了,这样我们就不用来来回回的改业务代码了,也就轻松的实现了系统模块间的解耦。
3.日志记录
我们大部分的日志记录行为其实是和前台用户操作的主业务没有直接关系的,只是我们的运营人和经营人员需要拿到这部分用户操作的日志信息,来进行用户行为分析或行为监控。在我们没有使用消息队列之前,笼统的做法是当有用户请求时,先处理用户的请求再记录日志,这两个操作是放在一起的,而前台用户也需要等待日志添加完成之后才能拿到后台的响应信息,这样其实浪费了前台用户的部分时间。此时我们可以使用消息队列,当响应完用户请求之后,只需要把这个操作信息放入消息队列之后,就可以直接返回结果给前台用户了,无须等待日志处理和日志添加完成,从而缩短了前台用户的等待时间。
如下图所示:
4.消息通讯
使用 MQ 可以作为消息通讯的实现手段,利用它可以实现点对点的通讯或者多对多的聊天室功能。
点对点的消息通讯如下图所示:
多对多的消息通讯如下图所示:
常用的 MQ 中间件
有 RabbitMQ、RocketMQ、Kafka 和 Redis 等,其中 Redis 属于轻量级的消息队列,而 RabbitMQ、RocketMQ、Kafka 属于比较成熟且比较稳定和高效的 MQ 中间件。
考点分析
MQ 属于中高级或优秀的程序员必备的技能,对于 MQ 中间件掌握的数量则是你技术广度和编程经验的直接体现信息之一。值得庆幸的是,关于 MQ 中间件的实现原理和使用方式都比较类似,因此如果开发者掌握一项 MQ 中间件再去熟悉其他 MQ 中间件时,会非常的容易。
MQ 相关的面试题还有这些:
- MQ 的特点是什么?引入 MQ 中间件会带来哪些问题?
- 常见的 MQ 中间件的优缺点分析。
知识扩展
MQ 的特点及注意事项
MQ 具有以下 5 个特点。
- 先进先出:消息队列的顺序一般在入列时就基本确定了,最先到达消息队列的信息,一般情况下也会先转发给订阅的消费者,我们把这种实现了先进先出的数据结构称之为队列。
- 发布、订阅工作模式:生产者也就是消息的创建者,负责创建和推送数据到消息服务器;消费者也就是消息的接收方,用于处理数据和确认消息的消费;消息队列也是 MQ 服务器中最重要的组成元素之一,它负责消息的存储,这三者是 MQ 中的三个重要角色。而它们之间的消息传递与转发都是通过发布以及订阅的工作模式来进行的,即生产者把消息推送到消息队列,消费者订阅到相关的消息后进行消费,在消息非阻塞的情况下,此模式基本可以实现同步操作的效果。并且此种工作模式会把请求的压力转移给 MQ 服务器,以减少了应用服务器本身的并发压力。
- 持久化:持久化是把消息从内存存储到磁盘的过程,并且在服务器重启或者发生宕机的情况下,重新启动服务器之后是保证数据不会丢失的一种手段,也是目前主流 MQ 中间件都会提供的重要功能。
- 分布式:MQ 的一个主要特性就是要应对大流量、大数据的高并发环境,一个单体的 MQ 服务器是很难应对这种高并发的压力的,所以 MQ 服务器都会支持分布式应用的部署,以分摊和降低高并发对 MQ 系统的冲击。
- 消息确认:消息消费确认是程序稳定性和安全性的一个重要考核指标,假如消费者在拿到消息之后突然宕机了,那么 MQ 服务器会误认为此消息已经被消费者消费了,从而造成消息丢失的问题,而目前市面上的主流 MQ 都实现了消息确认的功能,保证了消息不会丢失,从而保证了系统的稳定性。
引入 MQ 系统会带来的问题
任何系统的引入都是有两面性的,MQ 也不例外,在引入 MQ 之后,可能会带来以下两个问题。
- 增加了系统的运行风险:引入 MQ 系统,则意味着新增了一套系统,并且其他的业务系统会对 MQ 系统进行深度依赖,系统部署的越多则意味着发生故障的可能性就越大,如果 MQ 系统挂掉的话可能会导致整个业务系统瘫痪。
- 增加了系统的复杂度:引入 MQ 系统后,需要考虑消息丢失、消息重复消费、消息的顺序消费等问题,同时还需要引入新的客户端来处理 MQ 的业务,增加了编程的运维门槛,增加了系统的复杂性。
使用 MQ 需要注意的问题,不要过度依赖 MQ,比如发送短信验证码或邮件等功能,这种低频但有可能比较耗时的功能可以使用多线程异步处理即可,不用任何的功能都依赖 MQ 中间件来完成,但像秒杀抢购可能会导致超卖(也就是把货卖多了,库存变成负数了)等短时间内高并发的请求,此时建议使用 MQ 中间件。
常用的 MQ 中间件
常用的 MQ 中间件有 Redis、RabbitMQ、RocketMQ、Kafka,下来我们分别来看看各自的作用。
Redis 轻量级的消息中间件
Redis 是一个高效的内存性数据库中间件,但使用 Redis 也可以实现消息队列的功能。
早期的 Redis(Redis 5.0 之前)是不支持消息确认的,那时候我们可以通过 List 数据类型的 lpush 和 rpop 方法来实现队列消息的存入和读取功能,或者使用 Redis 提供的发布订阅(pub/sub)功能来实现消息队列,但这种模式不支持持久化,List 虽然支持持久化但不能设置复杂的路由规则来匹配多个消息,并且他们二者都不支持消息消费确认。
于是在 Redis 5.0 之后提供了新的数据类型 Stream 解决了消息确认的问题,但它同样不能提供复杂的路由匹配规则,因此在业务不复杂的场景下可以尝试性的使用 Redis 提供的消息队列。
RabbitMQ
RabbitMQ是一个实现了标准的高级消息队列协议(AMQP,Advanced Message Queuing Protocol)的老牌开源消息中间件,最初起源于金融系统,后来被普遍应用在了其他分布式系统中,它支持集群部署,和多种客户端调用。
RabbitMQ的优点:
- 支持持久化,RabbitMQ 支持磁盘持久化功能,保证了消息不会丢失;
- 高并发,RabbitMQ 使用了 Erlang 开发语言,Erlang 是为电话交换机开发的语言,天生自带高并发光环和高可用特性;
- 支持分布式集群,正是因为 Erlang 语言实现的,因此 RabbitMQ 集群部署也非常简单,只需要启动每个节点并使用 --link 把节点加入到集群中即可,并且 RabbitMQ 支持自动选主和自动容灾;
- 支持多种语言,比如 Java、.NET、PHP、Python、JavaScript、Ruby、Go 等;
- 支持消息确认,支持消息消费确认(ack)保证了每条消息可以被正常消费;
- 它支持很多插件,比如网页控制台消息管理插件、消息延迟插件等,RabbitMQ 的插件很多并且使用都很方便。
RabbitMQ集群分两种:普通集群、镜像集群
- 普通集群模式:
不同的节点之间只会相互同步元数据,队列不同步,只在自己的broker中,普通集群模式不能保证队列的高可用性,因为队列内容不会复制。如果节点失效将导致相关队列不可用
- 镜像队列模式:
消息内容会在镜像节点间同步,保证 100% 数据不丢失。在实际工作中也是用得最多的,并且实现非常的简单,一般互联网大厂都会构建这种镜像集群模式。
mirror 镜像队列,目的是为了保证 rabbitMQ 数据的高可靠性解决方案,主要就是实现数据的同步,一般来讲是 2-3个节点实现数据同步。对于 100% 数据可靠性解决方案,一般是采用3个节点。
RocketMQ
RocketMQ是由阿里捐赠给Apache的一款低延迟、高并发、高可用、高可靠的分布式消息中间件。经历了淘宝双十一的洗礼。RocketMQ既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。
RocketMQ作为一款纯java、分布式、队列模型的开源消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。
RocketMQ的优势:
- 支持事务型消息(消息发送和 DB 操作保持两方的最终一致性,RabbitMQ 和 Kafka 不支持)
- 支持结合 RocketMQ 的多个系统之间数据最终一致性(多方事务,二方事务是前提)
- 支持 18 个级别的延迟消息(Kafka 不支持)
- 支持指定次数和时间间隔的失败消息重发(Kafka 不支持,RabbitMQ 需要手动确认)
- 支持 Consumer 端 Tag 过滤,减少不必要的网络传输(即过滤由MQ完成,而不是由消费者完成。RabbitMQ 和 Kafka 不支持)
- 支持重复消费(RabbitMQ 不支持,Kafka 支持)
Kafka
Kafka 是 LinkedIn 公司开发的基于 ZooKeeper 的多分区、多副本的分布式消息系统,它于 2010 年贡献给了 Apache 基金会,并且成为了 Apache 的顶级开源项目。其中 ZooKeeper 的作用是用来为 Kafka 提供集群元数据管理以及节点的选举和发现等功能。
与 RabbitMQ 比较类似,一个典型的 Kafka 是由多个 Broker、多个生产者和消费者,以及 ZooKeeper 集群组成的,其中 Broker 可以理解为一个代理,Kafka 集群中的一台服务器称之为一个 Broker,其组成框架图如下所示:
Kafka VS RabbitMQ VS RocketMQ
性能
消息中间件的性能主要衡量吞吐量,Kafka的吞吐量比RabbitMQ要高出1~2个数量级,RabbitMQ的单机QPS在万级别,Kafka的单机QPS能够达到百万级别。RocketMQ单机写入TPS单实例约7万条/秒,单机部署3个Broker,可以跑到最高12万条/秒,消息大小10个字节,Kafka如果开启幂等、事务等功能,性能也会有所降低。
数据可靠性
Kafka与RabbitMQ都具备多副本机制,数据可靠性较高。RocketMQ支持异步实时刷盘,同步刷盘,同步Replication,异步Replication。
服务可用性
Kafka采用集群部署,分区与多副本的设计,使得单节点宕机对服务无影响,且支持消息容量的线性提升。RabbitMQ支持集群部署,集群节点数量有多种规格。RocketMQ是分布式架构,可用性高。
功能
Kafka与RabbitMQ都是比较主流的两款消息中间件,具备消息传递的基本功能,但在一些特殊的功能方面存在差异,RocketMQ在阿里集团内部有大量的应用在使用。
Kafka 和 RabbitMQ,RocketMQ 都支持分布式集群部署,并且都支持数据持久化和消息消费确认等 MQ 的核心功能,对于 MQ 的选型要结合自己团队本身的情况,从性能、稳定性及二次开发的难易程度等维度来进行综合的考量并选择。
总结
本文讲了 MQ 的常见使用场景,以及常见的 MQ 中间件(Redis、RabbitMQ、Kafka)及其优缺点分析;同时还了解了 MQ 的五大特点:先进先出、发布和订阅的模式、持久化、分布式和消息确认等;接着讲了 MQ 引入对系统可能带来的风险;最后讲了 MQ 在使用时需要注意的问题。希望对整体了解 MQ 系统有所帮助。
感兴趣,请关注公众号:<架构师成长之路> 阅读更多精彩文章
本文由mdnice多平台发布