题目:Kafka 消息的发送过程简单介绍一下?
难度:medium
标签:db/mq/kafka, mq/kafka, difficulty/medium
评分:⭐⭐⭐☆☆(3/5)
Kafka 消息的过发送的过程是这样的,由生产者发送消息,构建消息,将消息发送到 Broker 对应的 Topic 和 Partition 中。
订阅者再根据对应的 Object Partition 去读取对应的消息。
主流程了,然后具体过程是,生产者如果是要看你的需求,如果是要保证消息的一定可达,你需要做一些额外的操作。
关闭自动提交,然后只有在收到了对应的返回的时候才进行终止,否则进行重试。比如发送消息的时候,只有 Broker 返回发送成功了才结束,否则的话就进行重试。
消费者消费的时候也是一样的。消费消息关闭自动提交,然后只有收到了,处理完了之后才手动的提交,告知 Broker 这个消息收到了。对应的,另外需要保证防止消息重复,这时候需要设置一些幂等性的操作,比如通过消息的 ID 来确保这个消息是唯一的,还未消费过的,如果已经消费了,这个消息就进行丢弃。
优点:
不足:
Sender 线程 + RecordAccumulator + Partitioner,这是 Kafka 生产者的三大支柱acks=0/1/all 是消息可靠性的开关,acks=all + min.insync.replicas=N 才能真正保证消息不丢batch.size、linger.ms 是 Kafka 高吞吐的关键enable.auto.commit),Producer 侧不存在ProducerId + SequenceNumber,不是"消息 ID"补充:
enable.idempotence=true)+ 事务(transactional.id),可以做到 At-Least-Once 甚至 Exactly-Oncemin.insync.replicas 决定消息落几份副本才算 ack 成功核心考点定位:考察对 Kafka Producer 内部机制的理解,尤其是"异步批量 + 分区路由 + ack 语义"这条主链路,判断候选人是否真正在业务里用过 Kafka。
知识点拆解:
Sender:后台 IO 线程,负责实际发送请求RecordAccumulator:内存中的批次缓冲区(Deque),按 topic-partition 分桶Partitioner:决定消息路由到哪个 Partition,默认 hash(key) % numPartitionsMetadata:本地缓存的集群元数据(Topic → Partition → Leader Broker)send() → 序列化 → partitioner 选分区 → 放入 RecordAccumulator 对应桶Sender 线程从 Accumulator 拉批次 → 发送请求给 Leader BrokerLeaderReplication 线程异步复制给 Followeracks 语义返回:acks=0 直接返回、acks=1 本节点落盘返回、acks=all 等 ISR 全部落盘acks:0/1/all,可靠性 vs 性能权衡batch.size(默认 16KB)+ linger.ms(默认 0):批量发送的触发条件buffer.memory(默认 32MB):Accumulator 总容量,满了会阻塞 send()retries:发送失败重试次数max.inflight.requests.per.connection:影响是否乱序(>1 且未开启幂等会乱序)acks=0,可能丢acks=all + enable.idempotence=true(默认 true,Kafka 3.x),重试不会重复enable.idempotence=true + isolation.level=read_committed)Kafka 生产者的高性能 = 异步批量 + 分区路由 + 压缩;可靠性 = acks + 幂等 + 事务。
考察方向:
RecordAccumulator 的 Deque 结构、linger.ms 与 batch.size 的触发优先级、buffer.memory 满时抛 BufferExhaustedExceptionmax.inflight.requests.per.connection > 1 导致的乱序、Producer 端背压如何排查延伸递进面试题:
buffer.memory 满了会发生什么?send() 阻塞等待空闲批次,超过 max.block.ms 抛 TimeoutException;通常是生产者跟不上发送速率的背压信号acks=all 一定能保证消息不丢吗?min.insync.replicas + unclean.leader.election.enable=false面试官您好,Kafka 生产者从 send() 到消息落盘,核心是"异步批量 + 分区路由 + ack 语义"这三个词。
第一,Partitioner 决定分区。消息先经过 Partitioner,默认是 hash(key) % numPartitions,同一 key 一定落同一分区;kafka 3.x 引入了 sticky partitioner,key 为空时会让同一批消息粘到同一分区,减少分区切换的开销。
第二,RecordAccumulator 负责攒批。消息不会立刻发出去,而是先进入 Accumulator 里按 topic-partition 分桶的 Deque。Sender 后台线程按两个条件触发发送:批次大小达到 batch.size(默认 16KB)或者等待时间超过 linger.ms(默认 0,即不攒)。这两个参数是 Kafka 生产者吞吐的关键调优点,攒批大了吞吐高但延迟也会涨。
第三,Broker 端 acks 语义决定可靠性。acks=0 直接返回可能丢;acks=1 Leader 落盘返回;acks=all 等所有 ISR 副本落盘。要真正保证不丢,需要 acks=all + min.insync.replicas >= 2 + unclean.leader.election.enable=false,再叠加 enable.idempotence=true(0.11+)避免重试产生重复。消费者侧配合 isolation.level=read_committed 就是端到端 Exactly-Once。
当然,攒批和 acks=all 都是有代价的:吞吐和可靠性换延迟;实际业务要按场景权衡,不是越高越好。