为什么使用MQ?
使用MQ的场景很多,主要有三个:解耦、异步、削峰。
-
解耦:假设现在,日志不光要插入到数据库里,还要在硬盘中增加文件类型的日志,同时,一些关键日志还要通过邮件的方式发送给指定的人。那么,如果按照原来的逻辑,A可能就需要在原来的代码上做扩展,除了B服务,还要加上日志文件的存储和日志邮件的发送。但是,如果你使用了MQ,那么,A服务是不需要做更改的,它还是将消息放到MQ中即可,其它的服务,无论是原来的B服务还是新增的日志文件存储服务或日志邮件发送服务,都直接从MQ中获取消息并处理即可。这就是解耦,它的好处是提高系统灵活性,扩展性。
-
异步:可以将一些非核心流程,如日志,短信,邮件等,通过MQ的方式异步去处理。这样做的好处是缩短主流程的响应时间,提升用户体验。
-
削峰:MQ的本质就是业务的排队。所以,面对突然到来的高并发,MQ也可以不用慌忙,先排好队,不要着急,一个一个来。削峰的好处就是避免高并发压垮系统的关键组件,如某个核心服务或数据库等。
1.Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
开发语言 | java | erlang | java | scala |
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
社区活跃度 | 低 | 很高 | 一般 | 很高 |
-
中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;
-
大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。
-
大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,几乎是全世界这个领域的事实性规范。
2.消息队列的弊端有哪些?
数据延迟;增加系统复杂度;可能产生数据不一致的问题。
3.使用消息队列,怎么确保消息不丢失?
在生产阶段,你需要捕获消息发送的错误,并重发消息。 在存储阶段,你可以通过配置刷盘和复制相关的参数,让消息写入到多个副本的磁盘上,来确保消息不会因为某个 Broker 宕机或者磁盘损坏而丢失。 在消费阶段,你需要在处理完全部消费业务逻辑之后,再发送消费确认。
4.使用消息队列,如果处理重复消息?
1)利用数据库的唯一约束实现幂等 2)为更新的数据设置前置条件(CAS) 3)记录并检查操作(在发送消息时,给每条消息指定一个全局唯一的 ID,消费时,先根据这个 ID 检查这条消息是否有被消费过,如果没有消费过,才更新数据,然后将消费状态置为已消费。)
5.Kafka的消息是有序的吗?如果保证Kafka消息的顺序性?
Kafka只能保证局部有序,即只能保证一个分区里的消息有序。而其具体实现是通过生产者为每个分区的消息维护一个发送队列,我们需要将保证顺序的消息都发送到同一个分区中。并且由于Kafka会同时发送多个消息,所以还需指定max.in.flight.requests.per.connection为1,保证前一个消息发送成功,后一个消息才开始发送。
6.消息如何保证幂等性
例如kafka的offset可能是消费者批量处理后才提交到zk,重启后再消费时就可能会收到重复消息,需要消费者在处理消息时做幂等性设计,即先判断是否消费过,把已消费的放到本地缓存或者redis中,每次消费时先做个判断即可。
7.消息队列积压怎么办
当消费者出现异常,很容易引起队列积压,如果一秒钟1000个消息,那么一个小时就是几千万的消息积压,是非常可怕的事情,但是生产线上又有可能会出现;
当消息积压来不及处理,rabbitMQ如果设置了消息过期时间,那么就有可能由于积压无法及时处理而过期,这消息就被丢失了;
解决方法如下:
不建议在生产环境使用数据过期策略,一是数据是否丢失无法控制,二是一旦积压就很有可能丢失;建议数据的处理都有代码来控制;
当出现消息积压时,做法就是临时扩大consumer个数,让消息快速消费,一般都是通过业务逻辑的手段来完成:如下:
【rabbitmq解决积压范例】
修复consumer代码故障,确保consumer逻辑正确可以消费;
停止consumer,开启10倍20倍的queue个数; * 创建一个临时的consumer程序,消费积压的queue,并把消息写入到扩建的10倍queue中; * 再开启10倍20倍的consumer对新的扩充后队列进行消费; * 这种做法相当于通过物理资源扩充了10倍来快速消费;
当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费;
【kafka解决范例】
修复consumer代码故障,确保consumer逻辑正确可以消费;
停止consumer,新建topic,新建10倍20倍的partition个数; * 创建对应原topic的partition个数的临时的consumer程序,消费原来的topic,并把消息写入到扩建的新topic中; * 再开启对应新partition个数的consumer对新的topic进行消费; * 这种做法相当于通过物理资源扩充了10倍来快速消费;
当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费;