写在前面:
这篇文章是看Java工程师面试突击第1季(可能是史上最好的Java面试突击课程)-中华石杉老师
视频的一个总结,文章中第三方图片均归原作者所有。
什么是消息队列?
在计算机科学中,消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。
简单来说,消息队列是用来收发消息的。这里边的消息并不是单纯的指字符消息,也可以是一些字节序列等等。
有哪些常用的消息队列?
- Kafka
- ActiveMQ
- RabbitMQ
- RocketMQ
消息队列的优缺点?
优点
解耦
也就是说,减少了系统之间的耦合度。
异步
在数据库场景下 持久化请求异步的写入数据库中
削峰
消息队列存储了系统无法一次性响应的请求
缺点
- 系统可用性降低
- 系统复杂性提高
- 一致性问题
消息队列如何保证高可用?
RabbitMQ的高可用性
普通集群模式
每台机器上都会有元数据,但是只有一台机器上有你需要的数据,所以并不能保证其高可用。
镜像集群模式
这个架构解决了上一个普通集群模式的问题,但是消息大小会受到机器的限制。
Kafka的高可用性
Kafka是由多个broker组成的,每个broker是一个节点;
你创建一个topic,这个topic可以划分为多个partition,
每个partition可以存在于不同的broker上,每个partition就放一部分数据。
消息队列如何确保不消费重复消息?
幂等性
幂等性原本是数学上的概念,即使公式:f(x)=f(f(x)) 能够成立的数学性质。用在编程领域,则意为
对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的
也就是确保消息重复消费不会对业务产生影响
Kafka保证
RabbitMQ保证
RabbitMQ可以在每次消费数据时,将消息ID写入到集合中,这样每次新消息消费之前都在集合中找有没有这个数据,如果有就丢弃掉。
消息队列中的消息丢失怎么办?
RabbitMQ
生产者丢数据?
生产者将数据发送到rabbitmq的时候,可能数据就在半路给搞丢了,想不丢两种机制:
事务机制(阻塞)
Confirm模式(回调)
RabbitMQ弄丢了数据?
可以创建持久化操作。
创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里的数据
第二个是发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitmq就会将消息持久化到磁盘上去。
消费端弄丢了数据
关闭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(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了
生产者丢数据?
如果设置了acks=all 不会丢失
Kafka弄丢了数据?
设置参数
消费端弄丢了数据
关闭自动提交offset,在处理完之后自己手动提交offset,就可以保证数据不会丢
保证幂等性
如何保证消息队列中的消息的顺序?
RabbitMQ
拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点;或者就一个queue但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理
Kafka
一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue即可
消息队列中的消息积压问题?
一般来说,消费者炸了,消息才会积压。所以第一步应该修复消费者问题。
而且消息队列不要设置数据自动过期。
修复完成后,多弄几个消费者进行消费
消息中间件对比?
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 10万级,RocketMQ也是可以支撑高吞吐的一种MQ | 10万级别,这是kafka最大的优点,就是吞吐量高。 一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic数量对吞吐量的影响 | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降 这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic | topic从几十个到几百个的时候,吞吐量会大幅度下降 所以在同等机器下,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唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略 这个特性天然适合大数据实时计算以及日志收集 |