【面试题】消息队列相关

1. 为什么使用消息队列?

消息队列的使用场景:

  • 解耦:例如A系统需要发送数据到BCD等多个系统
  • 异步:例如A系统接收请求后不仅需要本地写库,还需要在BCD等多个系统中写库,然后才能返回结果。
  • 削峰:例如A系统在某个时间段内接收的请求多于平时好几倍(秒杀)

2. 使用消息队列会带来什么问题?

  • 可用性降低:需要考虑MQ服务器挂掉的情况。
    解决方法:MQ集群确保高可用,一个topic的数据,是分散放在多个机器(broker节点)上的,每个机器就放一部分数据(partition)。

  • 复杂性提高:需要考虑保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等问题。
    Kafka保证消息顺序执行的方法:一个topic、partition、consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue。

  • 数据一致性:消息队列带来的异步可以提高系统响应速度,如果A 系统处理完了直接返回成功,BCD三个系统某个系统写库失败,数据就会不一致。

3. 如何选择MQ?

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级 万级 10 万级(高吞吐) 10 万级(高吞吐)
topic 数量对吞吐量的影响 - - 达到几百/几千的级别,吞吐量会有较小幅度的下降 几十到几百个时,吞吐量会大幅度下降
时效性 ms 级 微秒级 ms 级 ms 级以内
可用性 高(主从架构) 高(主从架构) 非常高(分布式架构) 非常高(分布式架构)
消息可靠性 有较低的概率丢失数据 基本不丢 经过参数优化配置,可以做到 0 丢失 经过参数优化配置,可以做到 0 丢失
功能支持 MQ 领域的功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能
  • 使用MQ的场景主要是两大类:业务系统之间异步通信 和 大数据领域的实时数据计算
    • 一般业务系统之间通信采用RabbitMQ/RocketMQ,需要复杂的消息路由功能的支撑。
    • 大数据的实时计算和日志采集场景会采用Kafka,需要简单的消费模型、超高的吞吐量。

4. Kafka如何做到消息不丢失?

  • 一般至少需要配置4个参数
    • topic设置replication.factor=m(m>1),要求每个partition必须有至少2个副本。
    • 服务端:min.insync.replicas=n(n>1),要求一个leader需要感知到有至少一个follower跟自己保持联系。
    • 生产者:acks=all,要求每条数据,必须是写入所有replica之后,才能认为是写成功了。
    • 生产者:retries=MAX(无限重试),要求一旦写入失败,就无限重试
  • ACK 机制
    通过 ACK 机制保证消息送达。Kafka 采用的是至少一次(At least once),消息不会丢,但是可能会重复传输。
  • 发送消息
    为了得到更好的性能,Kafka 支持在生产者一侧进行本地buffer,也就是累积到一定的条数才发送,如果这里设置不当是会丢消息的。
    生产者端设置 producer.type=async/sync,默认是 sync。当设置为 async,会大幅提升性能,因为生产者会在本地缓冲消息,并适时批量发送。如果对可靠性要求高,那么这里可以设置为 sync 同步发送。
  • 消费消息
    如果更注重可靠性,则需要显示提交 Offset,也就是当所有业务都处理完成的时候,再提交 Offset。这样会导致重复消费,需要提供幂等性接口。

5. 为什么Kafka这么快?

  • 顺序写磁盘
    顺序写磁盘的性能是随机写入的几千倍,媲美内存随机访问的性能,磁盘不再是瓶颈点。
  • Page Cache
    为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。通过操作系统的Page Cache,Kafka的读写操作基本上是基于内存的,读写速度得到了极大的提升。
  • 零拷贝技术
    避免了程序和内核之间的拷贝,将数据从页面缓存直接发送到网络中,可以有效的减少上下文切换和拷贝次数。kafka的索引文件使用mmap+write 方式,data文件使用sendfile(java.nio.channels.FileChannel.transferTo方法)实现零拷贝。

6. 如何保证消息不被重复消费啊(幂等性)?

  • kafka有可能会出现消费重复消费的问题,kafka每个消息写进去,都有一个offset,代表他的序号,然后consumer消费了数据之后,每隔一段时间,会把自己消费过的消息的offset提交一下,代表已经消费过了,下次若是重启,就从上次消费到的offset继续消费。
  • 如果是直接kill进程,再重启系统,会导致consumer有些消息处理了,但是没来得及提交offset,重启之后,少数消息会再次消费一次。
  • 保证幂等性:使用数据库的唯一键或全局唯一id

你可能感兴趣的:(面试,kafka,面试)