写在前面: 这篇文章是看Java工程师面试突击第1季(可能是史上最好的Java面试突击课程)-中华石杉老师视频的一个总结,文章中第三方图片均归原作者所有。

什么是消息队列?

在计算机科学中,消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。

简单来说,消息队列是用来收发消息的。这里边的消息并不是单纯的指字符消息,也可以是一些字节序列等等。

有哪些常用的消息队列?

  1. Kafka
  2. ActiveMQ
  3. RabbitMQ
  4. RocketMQ

消息队列的优缺点?

优点

解耦

也就是说,减少了系统之间的耦合度。

解耦

异步

在数据库场景下 持久化请求异步的写入数据库中

异步

削峰

消息队列存储了系统无法一次性响应的请求

削峰

缺点

  1. 系统可用性降低
  2. 系统复杂性提高
  3. 一致性问题

消息队列如何保证高可用?

RabbitMQ的高可用性

普通集群模式

每台机器上都会有元数据,但是只有一台机器上有你需要的数据,所以并不能保证其高可用。

普通集群模式

镜像集群模式

这个架构解决了上一个普通集群模式的问题,但是消息大小会受到机器的限制。

镜像集群模式

Kafka的高可用性

Kafka是由多个broker组成的,每个broker是一个节点;

你创建一个topic,这个topic可以划分为多个partition,

每个partition可以存在于不同的broker上,每个partition就放一部分数据。

Kafka的高可用性

消息队列如何确保不消费重复消息?

幂等性

幂等性原本是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质。用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的

也就是确保消息重复消费不会对业务产生影响

Kafka保证

Kafka保证不消费重复消息

RabbitMQ保证

RabbitMQ可以在每次消费数据时,将消息ID写入到集合中,这样每次新消息消费之前都在集合中找有没有这个数据,如果有就丢弃掉。

消息队列中的消息丢失怎么办?

RabbitMQ

  1. 生产者丢数据?

    生产者将数据发送到rabbitmq的时候,可能数据就在半路给搞丢了,想不丢两种机制:

    • 事务机制(阻塞)

    • Confirm模式(回调)

  2. RabbitMQ弄丢了数据?

    可以创建持久化操作。

    • 创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里的数据

    • 第二个是发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitmq就会将消息持久化到磁盘上去。

  3. 消费端弄丢了数据

    关闭rabbitmq自动ack

保证消息不丢失

Kafka

设置一些参数避免丢数据问题:

  • 给这个topic设置replication.factor参数:这个值必须大于1,要求每个partition必须有至少2个副本
  • 在kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保leader挂了还有一个follower
  • 在producer端设置acks=all:这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了
  • 在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了
  1. 生产者丢数据?

    如果设置了acks=all 不会丢失

  2. Kafka弄丢了数据?

    设置参数

  3. 消费端弄丢了数据

    • 关闭自动提交offset,在处理完之后自己手动提交offset,就可以保证数据不会丢

    • 保证幂等性

保证消息不丢失

如何保证消息队列中的消息的顺序?

RabbitMQ

拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点;或者就一个queue但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理

Kafka

一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue即可

保证消息顺序

消息队列中的消息积压问题?

一般来说,消费者炸了,消息才会积压。所以第一步应该修复消费者问题。

而且消息队列不要设置数据自动过期。

修复完成后,多弄几个消费者进行消费

消息中间件对比?

特性ActiveMQRabbitMQRocketMQKafka
单机吞吐量万级,吞吐量比RocketMQ和Kafka要低了一个数量级万级,吞吐量比RocketMQ和Kafka要低了一个数量级10万级,RocketMQ也是可以支撑高吞吐的一种MQ10万级别,这是kafka最大的优点,就是吞吐量高。 一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic数量对吞吐量的影响topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降 这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topictopic从几十个到几百个的时候,吞吐量会大幅度下降 所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源
时效性ms级微秒级,这是rabbitmq的一大特点,延迟是最低的ms级延迟在ms级以内
可用性高,基于主从架构实现高可用性高,基于主从架构实现高可用性非常高,分布式架构非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性有较低的概率丢失数据经过参数优化配置,可以做到0丢失经过参数优化配置,消息可以做到0丢失
功能支持MQ领域的功能极其完备基于erlang开发,所以并发能力很强,性能极其好,延时很低MQ功能较为完善,还是分布式的,扩展性好功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准
优劣势总结非常成熟,功能强大,在业内大量的公司以及项目中都有应用 偶尔会有较低概率丢失消息 而且现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ 5.x维护越来越少,几个月才发布一个版本 而且确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用erlang语言开发,性能极其好,延时很低; 吞吐量到万级,MQ功能比较完备 而且开源提供的管理界面非常棒,用起来很好用 社区相对比较活跃,几乎每个月都发布几个版本分 在国内一些互联网公司近几年用rabbitmq也比较多一些 但是问题也是显而易见的,RabbitMQ确实吞吐量会低一些,这是因为他做的实现机制比较重。 而且erlang开发,国内有几个公司有实力做erlang源码级别的研究和定制?如果说你没这个实力的话,确实偶尔会有一些问题,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug。 而且rabbitmq集群动态扩展会很麻烦,不过这个我觉得还好。其实主要是erlang语言本身带来的问题。很难读源码,很难定制和掌控。接口简单易用,而且毕竟在阿里大规模应用过,有阿里品牌保障 日处理消息上百亿之多,可以做到大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性都是ok的,还可以支撑大规模的topic数量,支持复杂MQ业务场景 而且一个很大的优势在于,阿里出品都是java系的,我们可以自己阅读源码,定制自己公司的MQ,可以掌控 社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准JMS规范走的有些系统要迁移需要修改大量代码 还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ挺好的kafka的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展 同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量 而且kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略 这个特性天然适合大数据实时计算以及日志收集

消息中间件部署及比较:rabbitMQ、activeMQ、zeroMQ、rocketMQ、Kafka、redis

消息中间件对比