kafka 生产者是如何发送消息的?

Kafka 生产者发送消息的过程是一个异步流程,旨在实现高吞吐量和可靠性。这个过程主要涉及两个核心线程:主线程 和 Sender 线程。

下面是消息从创建到被 Broker 确认接收的详细步骤:

生产者发送消息的核心流程

  1. 创建消息 (主线程)

    用户首先需要创建一个 ProducerRecord 对象,它包含了消息需要发送到的 Topic 、分区 (Partition) (可选)、键 (Key) (可选)和 值 (Value)。

  2. 拦截器与序列化 (主线程)

    • 拦截器 (Interceptors): 在消息被序列化之前,它可以被一个或多个拦截器处理。用户可以自定义拦截器来修改消息内容或进行一些统计工作。
    • 序列化器 (Serializer): 由于消息是通过网络发送的,生产者需要将消息的 Key 和 Value 对象序列化成字节数组 (byte arrays)。 Kafka 客户端配置中必须指定 Key 和 Value 的序列化器。
  3. 分区器选择分区 (主线程)

    接下来,消息会经过分区器 (Partitioner) 来确定它将被发送到 Topic 的哪一个分区。 分区策略如下:

    • 指定分区: 如果在 ProducerRecord 中明确指定了分区号,那么分区器将直接使用该分区。
    • 使用消息的 Key: 如果没有指定分区但指定了 Key,Kafka 默认会使用 murmur2 哈希算法对 Key 进行哈希,然后根据哈希值对分区总数取模,从而为该 Key 选择一个固定的分区。 这保证了拥有相同 Key 的消息总能被发送到同一个分区,从而保证了消息在分区内的顺序性。
    • 未指定分区和 Key (粘性分区策略): 如果既没有指定分区也没有指定 Key,从 Kafka 2.4 版本开始,默认采用粘性分区策略 (Sticky Partitioner)。 生产者会随机选择一个分区并尽可能地向这个分区发送消息,直到该分区的批次(batch)满了或者等待时间到了,才会再随机选择下一个"粘性"分区。 这种策略减少了网络请求,降低了延迟并提升了吞吐量。在旧版本中,这里使用的是轮询策略 (Round-robin)。
  4. 消息累加器 (RecordAccumulator)

    确定分区后,消息并不会立即被发送出去,而是被放入一个消息累加器 (RecordAccumulator) 中。 这个累加器在生产者客户端内部为每个分区维护了一个双端队列 (Deque),消息会被追加到对应分区队列的批次 (ProducerBatch) 中。 这样做是为了将多个消息打包成一个批次进行发送,从而大大减少网络请求次数,提高吞吐量。

  5. Sender 线程发送消息

    Sender 是一个独立的 I/O 线程,它的主要工作是从消息累加器中获取准备好的消息批次,并将它们发送到对应的 Kafka Broker。 Sender 线程的发送时机由两个关键参数控制:

    • batch.size: 当累加到某个分区的消息大小总和达到这个值(默认为 16KB)时,Sender 线程就会发送这个批次。
    • linger.ms: 如果消息迟迟没有达到 batch.size,Sender 线程不会一直等待。当超过了 linger.ms 指定的时间(默认为 0ms)后,即使批次没满,也会被发送出去。

    因此,减小 batch.size 和 linger.ms 有利于降低延迟,而增大它们则有利于提升吞吐量。

  6. Broker 的响应与 Ack 确认机制

    当 Broker 收到消息后,会向生产者返回一个响应。这个响应的类型由生产者的 acks (acknowledgements) 参数决定,这是保证消息可靠性的核心机制:

    • acks=0: 生产者发送消息后不等待任何来自 Broker 的确认。 这种模式下延迟最低,吞吐量最高,但数据丢失的风险也最大(即"发后即忘")。
    • acks=1 (默认值): 生产者只需等待分区的 Leader 副本成功写入消息并返回确认即可。 这种模式在性能和可靠性之间取得了较好的平衡。但如果 Leader 副本写入成功后但在 Follower 副本同步完成前宕机,消息仍有丢失的风险。
    • acks=all (或 -1): 生产者需要等待 Leader 和所有 ISR (In-Sync Replicas, 同步副本列表) 中的 Follower 副本都成功写入消息后,才会收到确认。 这是最可靠的级别,可以最大限度地保证消息不丢失,但延迟也最高。
  7. 重试机制

    如果消息发送失败(例如网络抖动或 Broker 发生故障),生产者会根据配置的 retries 次数自动进行重试。 但需要注意的是,如果 max.in.flight.requests.per.connection 参数大于1,重试可能会导致消息乱序。

相关推荐
starzy19908 小时前
KafkaProducer 加载元数据:一条 MetadataRequest 的完整旅程
大数据·kafka
PingCAP10 小时前
善战者无赫赫之功:消费金融核心系统的数据库选型与升级路径
数据库·分布式·架构
库库的数据库笔记11 小时前
金融核心交易系统的数据库国产信创改造该如何做?
大数据·数据库·分布式·mysql·oracle·金融·数据库架构
RobinXYuan15 小时前
【Kafka 源码手记】04 客户端如何找到 Broker
kafka·源码阅读
爱吃火鸡面呀16 小时前
深入理解 Spark RDD:五大特性与实战解析
大数据·分布式·spark
Linux实战笔记18 小时前
记一次消息队列选型对比线上排查:Kafka/RabbitMQ/Pulsar 技术选型
kafka·rabbitmq·高并发·pulsar·消息队列选型
Norris Huang18 小时前
Kafka Connect 与 Flink CDC 写入 Iceberg:Upsert 的代价,以及 S3 Tables 的真正价值
分布式·flink·kafka
张彦峰ZYF18 小时前
一条消息的丢与重:Kafka 与 RabbitMQ 可靠投递的确认、位点、死信与幂等全链路
后端·kafka·消息队列·rabbitmq·isr·解耦与削峰·丢失与重复
我就是不信19 小时前
集群高并发环境下如何保证分布式唯一全局ID生成?
分布式
黄俊懿2 天前
【架构师从入门到进阶】第六章:服务集群优化——第八节:线程数公式
java·服务器·数据库·分布式·性能优化·架构·系统架构