java面试八股文--消息队列MQ

简述RabbitMQ的架构设计

Broker:rabbitmq的服务节点 

Queue:队列,是RabbitMQ的内部对象,用于存储消息。RabbitMQ中消息只能存储在队列中,生产者投递消息到队列,消费者从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(轮询)给多个消费者进行消费,而不是每个消费这都收到所有的消息进行消费(注意:RabbitMQ不支持队列层面的广播消费,如果需要广播消费,可以采用一个交换器通过路由Key绑定多个队列,由多个消费者来订阅这些队列的方式)

Exchange:交换器。生产者将消息发送给Exchage,由交换器将消息路由到一个或多个队列中。如果路由不到,或返回给生产者,或直接丢弃,或做其他处理。

RontingKey:路由Key。生产者将消息发送给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则。这个路由Key需要与交换器类型或绑定建(BindingKey)联合使用才能最终生效。在交换器类型和绑定健固定的情况下,生产者可以在发送消息给交换器时通过指定RoutingKey来决定消息流向哪里。

Binding:通过绑定将交换器和队列关联起来,在绑定的时候一般会指定一个绑定建,这样RabbitMQ就可以指定如何正确的路由到队列了。

交换器和队列实际上是多对多关系。就像关系数据库中的两张表。他们通过BingingKey做关联(多对多关系表)。在投递消息时,可以通过Exchange和RoutingKey(对应BindingKey)就可以找到相应的队列。

信道:信道是建立在Connection之上的虚拟连接。当应用程序与Rabbit Broker建立TCP连接的时候,客户端紧接着可以创建一个AMQP信道(Channel),每个信道都会被指派一个唯一的ID。RabbitMQ队里每条AMQP指令都是通过信道完成的。信道就像电缆里的光纤束。一条电缆内含有许多光纤束,允许所有的连接通过多条光纤束进行传输和接收。

RabbitMQ如何确保消息发送?消息接收?

发送方确认机制:

信道需要设置为confirm模式,则所有在信道上发布的消息都会分配一个唯一ID。

一旦消息被投递到queue(可持久化的消息需要写入磁盘),信道会发送一个确认给生产者(包含消息唯一ID)。

如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(未确认)消息给生产者。

所有被发送的消息都将被confirm(即 ack)或者被nack一次。但是没有对消息被confirm的快慢做任何保证,并且同一条消息不会既被confirm又被nack

发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者,生产者的回调方法会被触发。

confirmCallback接口:只确认是都正确到达Exchange中,成功到达则回调

ReturnCallback接口:消息失败返回时回调

接受放确认机制:

消费者在声明队列时,可以指定noAck参数,当noAck=false时,RabbitMQ会等待消费者显式发挥ack信号后才从内存(或者磁盘,持久化消息)中移去消息。否则,消息被消费后会被立即删除。

消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同的操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。

RabbitMQ不会为未ack的消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开。这么设计的原因是RabbitMQ允许消费者消费了一条消息的时长可以很长。保证数据的最终一致性:

如果消费者返回ack之前断开了连接,RabbitMQ会重新发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)

RabbitMQ事务消息

通过对信道的设置实现

  1. channel.txSelect();通知服务器开启事务模式;服务器端会返回Tx.Select-OK
  2. channel.basicPublish();发送消息,可以是多条,可以是消费消息提交ack
  3. channel.txCommit():提交事务
  4. channel.txRoolback():回滚事务

消费者使用事务:

  1. autoAck=false,手动提交ack,以事务提交或回滚为准;
  2. autoAck=true,不支持事务的,也就是说你即使在收到消息之后再回滚事务也是于事无补的,队列已经把消息移除了

如果其中一个环节chuxianwenti,就会跑出IoException异常,用户可以拦截异常进行事务回滚,或决定要不要重复消息

事务消息会降低rabbitMQ的性能

RabbitMQ死信队列、延时队列

  1. 消息被消费方否认确定,使用channel.basicNack 或 channel.basicReject,并且此时requeue属性被设置为false
  2. 消息在队列的存活时间超过设置的TTL时间
  3. 消息队列的消息数量已经超过最大队列长度

那么消息将成为“死信”。“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列消息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃

为每个需要使用死信的业务队列配置一个死信交换机,这里同一个项目的死信交换机可以共用一个,然后为每个业务队列分配一个单独的路由key,死信队列主不过是绑定在死信交换机上的队列,死信交换机也不是什么特殊的交换机,只不过是用来接受死信的缴环节,所以可以为任何类型(Direct,Fanout,Topic)

TTL:一条消息或者该队列中的所有消息的最大存活时间

如果一条消息设置了TTL属性或者进入了设置TTL属性的队列,那么这条消息如果在TTL设置的时间内没有被消费,则会成为“死信”。如果同时配置了队列的TTL和消息的TTL,那么较小的那个纸将会被使用。只需要消费者一直消费死信队列里的消息

RabbitMQ镜像队列机制

镜像queue有master节点和slave节点。master和slave是针对一个queue而言的,而不是node作为所有queue的master,其它node作为salve,一个queue第一次创建的node为它的master节点,其它node为slave节点。

无论客户端的请求打到master还是slave最终数据都是从master节点获取。当请求打到master节点使,master节点直接将消息返回给client,同时master节点会通过GM(Guaranteed Muliticast)协议将queue的最新状态广播给slave节点。GM保证了广播消息的原子性,即要么都更新要么都不更新。

当请求打到slave节点时,slave节点需要将请求先重定向到master节点,master节点将将消息返回给client,同时master节点会通过GM协议将queue的最新状态光波导slave节点。

如果没有新节点的加入,RabbitMQ不会同步之前的历史数据,新节点智慧复制该节点加入到集群之后新增的消息。

简述kafka架构设计

Consumer Group:消费者组,消费者组内每个消费者负责消费不同分区的数据,提高消费能力。逻辑上的一个订阅者。

Topic:可以理解为一个队列,Topic将消息分类,生产者和消费者面向的是同一个Topic

Partition:为了实现扩展性,提高并发能力,一个Topic以多个Partition的方式分不到多个Broker上,每个Partition是一个有序的队列。一个Topic的每个Partition都有若干个副本(Replica),一个Leader和若干个Follower,生产者发送数据的对象,以及消费者消费数据的对象,都是Leader。Follower负责实时从Leader中同步数据,保持和Leader数据的同步。Leader发生故障时,某个Follower还会成为新的Leader。

Offset:消费者消费的信息位置,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从消费位置继续消费。

Zookeeper:Kafka集群能够正常工作,需要依赖Zookeeper,Zookeeper帮助Kafka存储和管理集群信息。

kafka怎么处理消息顺序、重复发送、重复消费、消息丢失

一、消费的顺序性
现实场景

数据库中的binlog
一些业务需要,比如希望把某个订单的数据消费是有顺序的
问题描述

生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,写入同一个partion中的数据是一定有顺序的,如果是单线程是没有问题的,但是吞吐量太低了,但是如果是多线程是话,顺序就可能会乱掉。

解决办法

一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。
写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。

二、重复发送

生产发送的消息没有收到正确的broke响应,导致producer重试。

详解:producer发出一条消息,broker落盘以后,因为网络等原因,发送端得到一个发送失败的响应或者网络中断,然后producer收到 一个可恢复的Exception重试消息导致消息重复。

解决方案
启动kafka的幂等性

方案结论

enable.idempotence=true   //此时会默认开启acks=all
acks=all
retries>1

        每个生产者producer都有一个唯一id,producer每发送一条数据都会带上一个sequence,当消息落盘,sequence就会递增1。只需判断当前消息的sequence是否大于当前最大sequence,大于就代表此条数据没有落盘过,可以正常消费;不大于就代表落盘过,这个时候重发的消息会被服务端拒掉从而避免消息重复。
————————————————
版权声明:本题目内容为CSDN博主「IT利刃出鞘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/feiying0canglang/article/details/120514976


三、重复消费
消费方幂等操作,重复消费不会产生问题
对每个partitionID,产生一个uniqueID,.只有这个partition的数据被完全消费,才算成功,否则失败回滚。下次若重复执行,就skip
四、消息丢失
1.生产者数据不丢失

同步模式:配置=1(只有Leader收到,-1所有副本成功,0不等待)。leader partition挂了,数据就会丢失。

解决:设置为-1保证produce写入所有副本算成功
      producer.type=sync
      request.required.acks=-1

异步模式:当缓冲区满了,如果配置为0(没有收到确认,一满就丢弃),数据立刻丢弃

解决:不限制阻塞超时时间。就是一满生产者就阻塞
producer.type=async
request.required.acks=1
queue.buffering.max.ms=5000
queue.buffering.max.messages=10000
queue.enqueue.timeout.ms = -1
batch.num.messages=200

2.消费者数据不丢失 :流计算,基本数据源不适用。高级数据源以kafka为例,由2种方式:receiver(开启WAL,失败可恢复)和director(checkpoint保证)

3.若是storm在消费,开启storm的ackfail机制;若不是storm,数据处理完更新offset,低级API手动控制offset
4.Kafka发送数据过快,导致服务器网卡流量暴增。或磁盘过忙,出现丢包。
(1)  首先,对kafka进行限速,
(2)  其次启用重试机制,使重试间隔变长。
(3)  Kafka设置ack=all,即需要处于ISR(副本列表)的分区都确认,才算发送成功。       
                     props.put("compression.type", "gzip");
        props.put("linger.ms", "50");
        props.put("acks", "all")表示至少成功发送一次;
        props.put("retries ", 30);
        props.put("reconnect.backoff.ms ", 20000);
         props.put("retry.backoff.ms", 20000)
5.消费者速度很慢,导致一个session周期(0.1版本是默认30s)内未完成消费。导致心跳机制检测报告出问题。比如消费了的数据未及时提交offset,配置有可能是自动提交
问题场景:1.offset为自动提交,正在消费数据,kill消费者线程,下次重复消费。2.设置自动提交,关闭kafka,close之前,调用consumer.unsubscribed()则由可能部分offset没有提交。3.消费程序和业务逻辑在一个线程,导致offset提交超时。
————————————————
版权声明:本问题内容为CSDN博主「hy_coming」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hy_coming/article/details/117715165

Kafka是pull?push?优劣势分析?

pull模式:

  • 根据consumer的消费能力进行数据拉取,可以控制速率
  • 可以批量拉取,也可以单挑拉去
  • 可以设置不同的提交方式,实现不同的传输语义

缺点:如果kafka没有数据,会导致consumer空循环,消耗资源

解决:通过参数设置,consumer拉取数据为空或者没有达到一定数量时进行阻塞

push模式:不会导致consimer循环等待

缺点:速率固定,忽略了consumer的消费能力,可能导致拒绝服务或者网络阻塞等情况

Kafka中zk的作用

/brokers/ids:临时节点,保存所有broker节点信息,存储broker的物理地址、版本信息、启动时间等,节点名称为brokerID,broker定时发送心跳到zk,如果断开则该brokerID会被删除

/brokers/topics:临时节点,节点保存broker节点下所有的topic信息,每一个topic节点下包含一个固定的partitions节点,partitions的子节点就是topic的分区,每个分区下保存一个state节点,保存着当前leader分区和ISR的brokerID,state节点由leader创建,若leader宕机该节点会被删除,直到有新的leader选举产生,重新生成state节点

/consumers/[group_id]/owners/[topic]/[broker_id-partition_id] :维护消费者和分区的注册关系
/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id] :分区消息的消费进度 Offset
client通过topic找到topic树下的state节点,获取leader的brokerID,到broker树中找到broker的物理地址,但是client不会直连zk,而是通过配置的broker获取到zk的信息

简述kafka的rebalance机制

consumer group中的消费者与topic下的partion重新匹配的过程

何时会产生rebalance:

  • consumer group中的成员个数发生变化
  • consumer消费超时
  • group订阅的topic个数发生变化
  • group订阅的topic的分区数发生变化

coordinator:通常是partition的leader节点所在的broker,负责监控group中consumer的存活,consumer维持到coordinator的心跳,判断consumer的消费超时

  • coordinator通过心跳返回通知consumer进行rebalance
  • consumer请求coordinator加入组,coordinator选举产生leader consumer
  • leader consumer从coordinator获取所有的consumer,发送syncGroup(分配消息)给到coordinator
  • coordinator通过心跳机制将syncGroup下发给consumer
  • 完成rebalance

leader consumer监控topic的变化,通知coordinator出发rebalance

如果C1消费消息超时,出发rebalance,重新分配后,该消息会被其他消费者消费,此时C1消费完成提交offset,导致错误

解决:coordinator每次rebalance,会标记一个Generation给consumer,每次rebalance该Generation会+1,consumer提交offset时,coordinator会比对Generation,不一致则拒绝提交

Kafka的性能好在什么地方

kafka不基于内存,而是硬盘存储,因此消息堆积能力更强

顺序写:利用磁盘的顺序访问速度可以接近内存,kafka的消息都是append操作,partion是有序的,节省了磁盘的寻道时间,同时通过批次操作,节省写入次数,partition物理上分为多个segment存储,方便删除

传统:

  • 读取磁盘文件数据到内核缓冲区
  • 将内核缓冲区的数据copy到用户缓冲区
  • 将用户缓冲区的数据copy到socket的发送缓冲区
  • 将socket发送缓冲区中的数据发送到网卡,进行传输

零拷贝:

  • 直接将内核缓冲区的数据传送到网卡传输
  • 使用的操作系统的指令支持

kafka不太依赖jvm,主要理由操作系统的pageCache,如果生产消费速率相当,则直接用pageCache交换数据,不需要经过磁盘IO

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
面试官要求你回答一个关于 Python 的典型问题时,下面是一个可能的八股文回答模板: 首先,我会对问题进行理解和分析,确保我准确地理解了问题的要求和背景。 然后,我会按照以下结构来回答问题: 1. **问题背景和解决思路**:我会简要介绍问题的背景和解决思路。例如,如果问题是关于列表的操作,我可能会解释什么是列表以及如何使用它们来存储和操作数据。 2. **核心代码实现**:接下来,我会给出一个基本的代码实现,以展示我对问题的理解和能力。我会尽量保持代码简洁而清晰,并结合注释来解释代码的每一步。 3. **优化和扩展**:在展示基本实现之后,我还可以讨论如何优化代码以提高性能或满足更复杂的需求。我可以提出一些常见的优化策略,例如使用生成器、使用递归等。 4. **错误处理和异常处理**:此外,我还可以谈谈在代码中处理错误和异常的重要性。我会提到一些常见的错误和异常类型,并解释如何使用 try-except 语句来捕获和处理它们。 5. **应用场景和实际例子**:最后,我会给出一些实际的应用场景和示例,以展示我对 Python 的灵活运用能力。我可以谈论一些常见的 Python 库和框架,以及它们在实际项目中的使用案例。 通过以上结构,我可以清晰地展示我对 Python 的理解、能力和经验,并给面试官留下一个积极的印象。当然,在回答问题时,我也会根据具体问题的要求进行适当调整和补充。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值