消息队列MQ介绍,主流MQ对比
为什么我们要用到消息队列?
什么是消息队列?
消息队列(Message Queue),简称为MQ。
从字面看分为消息(Message)和队列(Queue)两块。
-
消息:指在应用之间需要传输的数据。一般只包含字符串信息,也可以定义复杂的格式。
-
队列:队列是一种先进先出的数据结构,队列中的元素都遵循“先进先出(FIFO)”的原则。
为什么需要消息队列?
今天我们要解决的一个实际工作中的业务需求场景问题:如何将M条的消息或者任务,尽可能的保证平均分发给N个消费者进行处理。业务上要求保证尽可能的消息的平均分布,同时消息队列中的同一条消息,必须保证只能给一个消费者进行消费处理,即不允许被重复进行消费。通俗一点就是我们要解决鸡蛋与篮子的存放数学问题:如何将10(M)个鸡蛋放在5(N)个篮子中呢?
使用MQ的处理逻辑消息队列的应用场景
应用耦合:解耦是消息队列要解决的最本质问题。
比如:
-
用创建订单的业务举例,用户发起在创建订单时,系统会先创建订单,然后会做一些其他相关的操作,例如扣除库存、扣除优惠券、增加积分等等操作。
-
这里有个问题就是假如在遇到执行扣除库存失败时,会影响到整个创建订单的业务,导致用户的订单创建失败。还存在问题就是订单的创建逻辑和库存、优惠券、积分这些业务过度的耦合,导致其中一个有改动都会牵扯到其他的业务逻辑。
怎么来解决上述的问题呢?可以使用到消息中间件来处理。
改造后:
-
用户在发起创建订单请求时,系统先创建用户的订单信息,创建成功后,发送扣除库存、优惠券、增加积分也消息到队列中,然后返回创建的订单信息给用户。库存系统从消息队列中拉取消息来扣除库存。
-
这里在创建订单的逻辑中,就不在去关心库存是否扣除成功、优惠券是否扣除等业务的执行情况。
异步消息:当一次请求在处理复杂的业务逻辑,多个业务逻辑都在同步执行会导致业务处理耗时太过长。使用消息中间件将一部分不需要及时同步处理的业务放到消息队列中来处理,从而提高了业务的处理速度。
比如:用户在发起一次创建订单的请求时,系统会先创建用户订单(耗时50ms)、扣除库存(耗时50ms)、扣除优惠券(耗时50ms)、增加积分(耗时50ms)等等业务,这里一次情况需要完成这么多业务然后才返回到用户这边,通知用户订单创建成功,这一系列耗时就已经超过200ms了。
业务耦合如果改成使用消息中间的方式来处理,用户发起请求时,系统将创建用户订单,然后把扣除库存、扣除优惠券、增加积分的操作放到队列中,返回通知用户订单创建成功,由消费系统在来处理扣除库存、扣除优惠券、增加积分等等操作。这样子大大增加系统的处理效率,提升了系统的吞吐量。
业务解耦流量削锋:在高并发请求的时候,所有的请求都直接打到数据库中,容易造成数据库的压力。如果使用到消息中间件,所有的请求过来都是先发到消息队列中,然后业务系统再慢慢从消息队列中消费消息。请求高并发的时候允许消息的积压处理,以便造成业务系统的负载。
比如:秒杀系统中,在传统模式中并发量大的时候,所有的请求都一下子打到数据中容易造成数据库的异常。
传统模式的秒杀使用消息中间件来处理,将所有的秒杀请求都先发送到MQ中,由各个消费系统按照数据库能处理的并发量,从消息队列中慢慢的拉取消息进行处理,保证系统的稳定性。
使用MQ后最终一致性:最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。
最终一致性不是消息队列的必备特性,但确实可以依靠消息队列来做最终一致性的事情。
消息队列有什么缺点
-
系统可用性降低
-
系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用
-
系统复杂度提高
-
硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。
-
一致性问题
-
A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。
主流的消息队列对比
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
Kafka的优缺点
优点:
-
高吞吐量:即使在非常廉价的机器上,Kafka也能做到每秒处理几十万条消息,而它的延迟最低只有几毫秒。
-
低延迟:延迟可以控制在ms以内。
-
持久性:Kafka可以将消息直接持久化在普通磁盘上,且磁盘读写性能优异。
-
扩展性。Kafka集群支持热扩展,Kaka集群启动运行后,用户可以直接向集群中添加。
-
容错性。Kafka会将数据备份到多台服务器节点中,即使当某个服务器节点失效时,Zookeeper将通知生产者和消费者从而使用其他的节点,也不会影响整个系统的功能。
-
支持多种客户端语言。Kafka支持Java、.NET、PHP、Python等多种语言。
缺点:
-
重复消息:Kafka保证每条消息至少送达一次,虽然几率很小,但一条消息可能被送达多次。
-
消息乱序:Kafka某一个固定的Partition内部的消息是保证有序的,如果一个Topic有多个Partition,partition之间的消息送达不保证有序。
-
复杂性:Kafka需要Zookeeper的支持,Topic一般需要人工创建,部署和维护比一般MQ成本更高。
-
topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源。
RabbitMQ的优缺点
优点:
-
持久化:RabbitMQ可以保证所在的服务器宕机后,消息不会丢失。
-
高可用:部分机器宕机了还可以继续使用。
-
高级功能:如消息重试、死信队列等
缺点:
-
首先是RabbitMQ吞吐量比较低,大概在每秒几万的样子,这样像对于大型电商促销秒杀就不能胜任。
-
集群线性扩展比较麻烦。
-
开发语言是erlang,懂得人不是很多,无法对其改造。
RocketMQ的优缺点
优点:
-
高吞吐量:大概普通机器有十万QPS往上。
-
消息可靠性:
-
生产者的可靠性保证:生产者发送消息后返回SendResult,如果isSuccess返回true,则表示消息已经确认发送到服务器并被服务器接收保存。整个发送过程是一个同步过程。
-
服务器的可靠性:消息生产者发送的消息,RocketMQ服务收到后在做必要的校验和检查之后马上保存到磁盘,写入成功后返回给生产者。因此可以确认每条发送结果为成功的消息都会被消息服务器写入磁盘。
-
消费者的可靠性:消费者是一条一条顺序消费的,之后在成功消费一条后才会消费吓一跳。如果在消费某一条消息时失败则会重试消费这条消息,默认为5次,如果超过最大次数仍然无法消费,则将消息保存到本地,后台线程继续重试消费,主线程则会继续往后走,消费队列后面的消息。
-
-
消息持久性:RocketMQ收到消息后,会将消息持久化到文件,并利用Linux文件系统内存来提高性能
-
消息实时性:RocketMQ采取长轮询+PULL模式保证消息的实时性
-
消息堆积:支持10亿级别的消息堆积,不会因为消息堆积影响性能
-
高级功能:如延迟消息、消息回朔等
-
使用的java开发
缺点:
-
消息重复:对于消费者来说,通过拉取方式将消息保存到本地,消费完再向服务器返回,在网络异常的情况下可能会出现重复。
-
消息过滤:
-
服务器端过滤:减少不必要消息传输,但是会增加服务器负担
-
客户端过滤:根据客户端需求来定制消息,缺点是客户端会收到对它来说没用的消息,如果客户端无法承载这么多消息就会导致故障