自学内容网 自学内容网

MQ篇【Java面试】

Java全技术栈面试题合集地址

Java全技术栈面试题合集地址跳转

MQ篇

1.什么是 rabbitmq

采用 AMQP 高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦。

2.为什么要使用 rabbitmq

  1. 在分布式系统下具备异步,削峰,负载均衡等一系列高级功能。
  2. 拥有持久化的机制,进程消息,队列中的信息也可以保存下来。
  3. 实现消费者和生产者之间的解耦。
  4. 对于高并发场景下,利用消息队列可以使得同步访问变为串行访问达到一定量的限流,利于数据库的操作。
  5. .可以使用消息队列达到异步下单的效果,排队中,后台进行逻辑下单。

3.使用 rabbitmq 的场景

  1. 服务间异步通信
  2. 顺序消费
  3. 定时任务
  4. 请求削峰

4.如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消费了消息?

发送方确认模式

  1. 将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的 ID。
  2. 一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。
  3. 如果 RabbitMQ 发生内部错误从而导致消息丢失,会发送一条 nack(not acknowledged,未确认)消息。
  4. 发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
    接收方确认机制
  5. 消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。这里并没有用到超时机制,RabbitMQ 仅通过 Consumer 的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ 给了 Consumer 足够长的时间来处理消息。保证数据的最终一致性。

几种特殊情况需要应用层特殊处理

  • 如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ 会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
  • 如果消费者接收到消息却没有确认消息,连接也未断开,则 RabbitMQ 认为该消费者繁忙,将不会给该消费者分发更多的消息。

5.如何避免消息重复投递或重复消费?

在消息生产时,MQ 内部针对每条生产者发送的消息生成一个 inner-msg-id,作为去重的依据(消息投递失败并重传),避免重复的消息进入队列;
在消息消费时,要求消息体中必须要有一个 bizId(对于同一业务全局唯一,如支付 ID、订单 ID、帖子 ID 等)作为去重的依据,避免同一条消息被重复消费。

6.消息基于什么传输?

由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。

7.消息如何分发?

若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。通过路由可实现多消费的功能

8.消息怎么路由?

  1. 消息提供方->路由->一至多个队列
  2. 消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
  3. 通过队列路由键,可以把队列绑定到交换器上。
  4. 消息到达交换器后,RabbitMQ 会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。
    交换器
  5. fanout(广播):如果交换器收到消息,将会广播到所有绑定的队列上。
  6. direct(单播):如果路由键完全匹配,消息就被投递到相应的队列.
  7. topic(模式匹配):可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符。
  8. Headers:Headers Exchange不同于上面三种Exchange,它是根据Message的一些头部信息来分发过滤Message,忽略routing key的属性,如果Header信息和message消息的头信息相匹配,那么这条消息就匹配上了。已经弃用了。

9.如何确保消息不丢失?

消息持久化,当然前提是队列必须持久化
RabbitMQ 确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit 会在消息提交到日志文件后才发送响应。
一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ 会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前 RabbitMQ 重启,那么 Rabbit 会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。

10 . 基础架构

  • message
    消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括 routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。

  • Publisher
    消息的生产者,也是一个向交换器发布消息的客户端应用程序。

  • Exchange(将消息路由给队列 )
    交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。

  • Binding(消息队列和交换器之间的关联)
    绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。

  • Queue
    消息队列, 用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。

  • Connection
    网络连接,比如一个 TCP 连接。

  • Channel
    信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的 TCP 连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。

  • Consumer
    消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

  • Virtual Host
    虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密

  • Broker
    表示消息队列服务器实体。

11.死信队列?

DLX,全称为 Dead-Letter-Exchange,死信交换器,死信邮箱。当消息在一个队列中变成死信 (dead message) 之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX,绑定 DLX 的队列就称之为死信队列。

12.导致的死信的几种原因?

  • 消息被拒(Basic.Reject /Basic.Nack) 且 requeue = false。
  • 消息TTL过期。
  • 队列满了,无法再添加。

13.延迟队列?

存储对应的延迟消息,指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。

14.事务机制

  • RabbitMQ 客户端中与事务机制相关的方法有三个:
  • channel.txSelect 用于将当前的信道设置成事务模式。
  • channel . txCommit 用于提交事务 。
  • channel . txRollback 用于事务回滚,如果在事务提交执行之前由于 RabbitMQ 异常崩溃或者其他原因抛出异常,通过txRollback来回滚。
  • 不能和发送确认机制同时存在

15.发送确认机制

生产者把信道设置为confirm确认模式,设置后,所有再改信道发布的消息都会被指定一个唯一的ID,一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者(包含消息的唯一ID),这样生产者就知道消息到达对应的目的地了。

16.消息传输保证层级?

  • At most once:最多一次。消息可能会丢失,但不会重复传输。
  • At least once:最少一次。消息绝不会丢失,但可能会重复传输。
  • Exactly once: 恰好一次,每条消息肯定仅传输一次。

17.了解Virtual Host吗?

每一个RabbitMQ服务器都能创建虚拟的消息服务器,也叫虚拟主机(virtual host),简称vhost。
默认为“/”。

18.集群中的节点类型

  • 内存节点:ram,将变更写入内存。
  • 磁盘节点:disc,磁盘写入操作。
  • RabbitMQ要求最少有一个磁盘节点。

19.队列结构?

  • rabbit_amqqueue_process:负责协议相关的消息处理,即接收生产者发布的消息、向消费者交付消息、处理消息的确认(包括生产端的 confirm 和消费端的 ack) 等。
  • backing_queue:是消息存储的具体形式和引擎,并向 rabbit amqqueue process提供相关的接口以供调用。

20. 如何保证RabbitMQ消息队列的高可用?

RabbitMQ 有三种模式:单机模式,普通集群模式,镜像集群模式。

  • 单机模式:就是demo级别的,一般就是你本地启动了玩玩儿的,没人生产用单机模式
  • 普通集群模式:意思就是在多台机器上启动多个RabbitMQ实例,每个机器启动一个。
  • 镜像集群模式:这种模式,才是所谓的RabbitMQ的高可用模式,跟普通集群模式不一样的是,你创建的queue,无论元数据(元数据指RabbitMQ的配置数据)还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。

21.设计MQ思路

首先这个mq得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下kafka的设计理念,broker->topic->partition,每个partition放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给topic增加partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?

其次你得考虑一下这个mq的数据要不要落地磁盘吧?那肯定要了,落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。

Kafka

22.kafka中consumer 是推还是拉?

producer 将消息推送到 broker,consumer 从broker 拉取消息。

23.Kafka 判断一个节点是否还活着有那两个条件?

  1. 节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接。
  2. 如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久。

24.Kafka 与传统 MQ 消息系统之间有三个关键区别

  1. Kafka 持久化日志,这些日志可以被重复读取和无限期保留。
  2. Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据提升容错能力和高可用性。
  3. Kafka 支持实时的流式处理。

25.讲一讲 kafka 的 ack 的三种机制

request.required.acks 有三个值 0 1 -1(all)

  • 0:生产者不会等待 broker 的 ack,这个延迟最低但是存储的保证最弱当 server 挂掉的时候就会丢数据。
  • 1:服务端会等待 ack 值 leader 副本确认接收到消息后发送 ack 但是如果 leader挂掉后他不确保是否复制完成新 leader 也会导致数据丢失。
  • -1(all):服务端会等所有的 follower 的副本受到数据后才会受到 leader 发出的ack,这样数据不会丢失。

25.消费者如何不自动提交偏移量,由应用提交?

将 auto.commit.offset 设为 false,然后在处理一批消息后 commitSync() 或者异步提交 commitAsync()

27.消费者故障,出现活锁问题如何解决?

出现“活锁”的情况,是它持续的发送心跳,但是没有处理。为了预防消费者在这种情况下一直持有分区,我们使用 max.poll.interval.ms 活跃检测机制。 在此基础上,如果你调用的 poll 的频率大于最大间隔,则客户端将主动地离开组,以便其他消费者接管该分区。 发生这种情况时,你会看到 offset 提交失败(调用commitSync()引发的CommitFailedException)。这是一种安全机制,保障只有活动成员能够提交 offset。所以要留在组中,你必须持续调用 poll。
消费者提供两个配置设置来控制 poll 循环:

  • max.poll.interval.ms:增大 poll 的间隔,可以为消费者提供更多的时间去处理返回的消息(调用 poll(long)返回的消息,通常返回的消息都是一批)。缺点是此值越大将会延迟组重新平衡。
  • max.poll.records:此设置限制每次调用 poll 返回的消息数,这样可以更容易的预测每次 poll 间隔要处理的最大值。通过调整此值,可以减少 poll 间隔,减少重新平衡分组的。

对于消息处理时间不可预测地的情况,这些选项是不够的。 处理这种情况的推荐方法是将消息处理移到另一个线程中,让消费者继续调用 poll。 但是必须注意确保已提交的 offset 不超过实际位置。另外,你必须禁用自动提交,并只有在线程完成处理后才为记录手动提交偏移量(取决于你)。 还要注意,你需要 pause 暂停分区,不会从 poll 接收到新消息,让线程处理完之前返回的消息(如果你的处
理能力比拉取消息的慢,那创建新线程将导致你机器内存溢出)。

28.如何控制消费的位置

kafka 使用 seek(TopicPartition, long)指定新的消费位置。用于查找服务器保留的最早和最新的 offset 的特殊的方法也可用(seekToBeginning(Collection) 和seekToEnd(Collection))。

29.kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?

Kafka 分布式的单位是 partition,同一个 partition 用一个 write ahead log 组织,所以可以保证 FIFO 的顺序。不同 partition 之间不能保证顺序。但是绝大多数用户都可以通过 message key 来定义,因为同一个 key 的 message 可以保证只发送到同一个 partition。
Kafka 中发送 1 条消息的时候,可以指定(topic, partition, key) 3 个参数。partiton 和 key 是可选的。如果你指定了 partition,那就是所有消息发往同 1个 partition,就是有序的。并且在消费端,Kafka 保证,1 个 partition 只能被
1 个 consumer 消费。或者你指定 key(比如 order id),具有同 1 个 key 的所有消息,会发往同 1 个 partition。

30.kafka 如何不消费重复数据?比如扣款,我们不能重复的扣。

  1. 根据业务订单唯一id去数据库查一下,或者使用唯一键,插入重复数据就会报错
  2. 根据业务订单唯一id去redis查一下,查完之后id写入redis

31.Kafka 都有哪些特点?

高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。

  • 可扩展性:kafka集群支持热扩展
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
  • 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
  • 高并发:支持数千个客户端同时读写

32. Kafka 是如何实现高吞吐率的?

Kafka是分布式消息系统,需要处理海量的消息,Kafka的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。kafka主要使用了以下几个方式实现了超高的吞吐率:

  • 顺序读写;
  • 零拷贝
  • 文件分段
  • 批量发送
  • 数据压缩。

33.Kafka 分区数可以增加或减少吗?为什么?

我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。

34.消费者提交消费位移时提交的是当前消费到的最新消息的offset还是offset+1?

offset+1

35. kafka 的零拷贝原理?

在实际应用中,如果我们需要把磁盘中的某个文件内容发送到远程服务器上

  • 从磁盘中读取目标文件内容拷贝到内核缓冲区
  • CPU 控制器再把内核缓冲区的数据赋值到用户空间的缓冲区中
  • 接着在应用程序中,调用 write()方法,把用户空间缓冲区中的数据拷贝到内核下的Socket Buffer 中。
  • 最后,把在内核模式下的 SocketBuffer 中的数据赋值到网卡缓冲区(NIC Buffer)网卡缓冲区再把数据传输到目标服务器上。

在这个过程中我们可以发现,数据从磁盘到最终发送出去,要经历 4 次拷贝,而在这四次拷贝过程中,有两次拷贝是浪费的,分别是:
从内核空间赋值到用户空间
从用户空间再次复制到内核空间
除此之外,由于用户空间和内核空间的切换会带来 CPU 的上线文切换,对于 CPU 性能也会造成性能影响。

原理
零拷贝通过DMA(Direct Memory Access)技术把文件内容复制到内核空间中的Read Buffer,
接着把包含数据位置和长度信息的文件描述符加载到Socket Buffer 中,DMA 引擎直接可以把数据从内核空间中传递给网卡设备。
在这个流程中,数据只经历了两次拷贝就发送到了网卡中,并且减少了 2 次cpu 的上下文切换,对于效率有非常大的提高。

所以,所谓零拷贝,并不是完全没有数据赋值,只是相对于用户空间来说,不再需要进行数据拷贝。对于前面说的整个流程来说,零拷贝只是减少了不必要的拷贝次数而已。


原文地址:https://blog.csdn.net/qq_58353992/article/details/136917703

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!