大白话八股文-Kafka

一.Kafka的系统架构

如上图所示,Kafka由Producer、Broker、Consumer 以及负责集群管理的 ZooKeeper 组成,各部分功能如下:

  • Producer:生产者,负责消息的创建并通过一定的路由策略发送消息到合适的 Broker
  • Broker:服务实例,负责消息的持久化、中转等功能;
  • Consumer:消费者,负责从 Broker 中拉取(Pull)订阅的消息并进行消费,通常多个消费者构成一个分组,消息只能被同组中的一个消费者消费;
  • ZooKeeper:负责 broker、consumer 集群元数据的管理等;

上图消息流转过程中,还有几个特别重要的概念—主题(Topic)、分区(Partition)、分段(segment)、位移(offset)。

  • topic:消息主题。Kafka 按 topic 对消息进行分类,我们在收发消息时只需指定 topic。
  • partition:分区。为了提升系统的吞吐,一个 topic 下通常有多个 partition,partition 分布在不同的 Broker 上,用于存储 topic 的消息,这使 Kafka 可以在多台机器上处理、存储消息,给 kafka 提供给了并行的消息处理能力和横向扩容能力。另外,为了提升系统的可靠性,partition 通常会分组,且每组有一个主 partition、多个副本 partition,且分布在不同的 broker 上,从而起到容灾的作用。
  • segment:分段。宏观上看,一个 partition 对应一个日志(Log)。由于生产者生产的消息会不断追加到 log 文件末尾,为防止 log 文件过大导致数据检索效率低下,Kafka 采取了分段和索引机制,将每个 partition 分为多个 segment,同时也便于消息的维护和清理。每个 segment 包含一个 .log 日志文件、两个索引(.index、timeindex)文件以及其他可能的文件。每个 Segment 的数据文件以该段中最小的 offset 为文件名,当查找 offset 的 Message 的时候,通过二分查找快找到 Message 所处于的 Segment 中。
  • offset:消息在日志中的位置,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量。offset 是消息在分区中的唯一标识,是一个单调递增且不变的值。Kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,也就是说,Kafka 保证的是分区有序而不是主题有序。

二.Kafka的高可靠性(不丢失)

Kafka 高可靠性的核心是保证消息在传递过程中不丢失,涉及如下核心环节:

  1. 消息从生产者可靠地发送至 Broker;-- 网络、本地丢数据。
  2. 发送到 Broker 的消息可靠持久化;-- PageCache 缓存落盘、单点崩溃、主从同步跨网络
  3. 消费者从 Broker 消费到消息且最好只消费一次 -- 跨网络消息传输。

如何保障消息从生产者可靠地发送至 Broker

Producer 发送消息后,能够收到来自 Broker 的消息保存成功 ack;

  • Request.required.acks = 0:请求发送即认为成功,不关心有没有写成功,常用于日志进行分析场景。
  • Request.required.acks = 1:当 leader partition 写入成功以后,才算写入成功,有丢数据的可能。
  • Request.required.acks= -1:ISR 列表里面的所有副本都写完以后,这条消息才算写入成功,强可靠性保证。

        为了实现强可靠的 kafka 系统,我们需要设置 Request.required.acks= -1,同时还会设置集群中处于正常同步状态的副本 follower 数量 min.insync.replicas>2,另外,设置 unclean.leader.election.enable=false 使得集群中 ISR 的 follower 才可变成新的 leader,避免特殊情况下消息截断的出现。

三.Kafka的消息持久化

为了确保 Producer 收到 Broker 的成功 ack 后,消息一定不在 Broker 环节丢失,我们核心要关注以下几点:

  • Broker 返回 Producer 成功 ack 时,消息是否已经落盘;
  • Broker 宕机是否会导致数据丢失,容灾机制是什么;
  • Replica 副本机制带来的多副本间数据同步一致性问题如何解决;

Broker 异步刷盘机制

        kafka 为了获得更高吞吐,Broker 接收到消息后只是将数据写入 PageCache 后便认为消息已写入成功,而 PageCache 中的数据通过 linux 的 flusher 程序进行异步刷盘(刷盘触发条:主动调用 sync 或 fsync 函数、可用内存低于阀值、dirty data 时间达到阀值),将数据顺序写到磁盘。由于消息是写入到 PageCache ,单机场景,如果还没刷盘 Broker 就宕机了,那么 Producer 产生的这部分数据就可能丢失。为了解决单机故障可能带来的数据丢失问题,Kafka 为分区引入了副本机制。

Replica 副本机制

        Kafka 每组分区通常有多个副本,同组分区的不同副本分布在不同的 Broker 上,保存相同的消息(可能有滞后)。副本之间是“一主多从”的关系,其中 leader 副本负责处理读写请求,follower 副本负责从 leader 拉取消息进行同步。分区的所有副本统称为 AR(Assigned Replicas),其中所有与 leader 副本保持一定同步的副本(包括 leader 副本在内)组成ISR(In-Sync Replicas),与 leader 同步滞后过多的副本组成 OSR(Out-of-Sync Replicas),由此可见,AR=ISR+OSR。follower 副本是否与leader同步的判断标准取决于 Broker 端参数 replica.lag.time.max.ms(默认为10秒),follower 默认每隔 500ms 向 leader fetch 一次数据,只要一个 Follower 副本落后 Leader 副本的时间不连续超过10秒,那么 Kafka 就认为该 Follower 副本与 leader 是同步的。在正常情况下,所有的 follower 副本都应该与 leader 副本保持一定程度的同步,即 AR=ISR,OSR 集合为空。

        当 leader 副本所在 Broker 宕机时,Kafka 会借助 ZK 从 follower 副本中选举新的 leader 继续对外提供服务,实现故障的自动转移,保证服务可用。为了使选举的新 leader 和旧 leader 数据尽可能一致,当 leader 副本发生故障时,默认情况下只有在 ISR 集合中的副本才有资格被选举为新的 leader,而在 OSR 集合中的副本则没有任何机会(可通过设置unclean.leader.election.enable改变)。

        当 Kafka 通过多副本机制解决单机故障问题时,同时也带来了多副本间数据同步一致性问题。Kafka 通过高水位更新机制、副本同步机制、 Leader Epoch 等多种措施解决了多副本间数据同步一致性问题,下面我们来依次看下这几大措施。

HW 和 LEO

  • HW: High Watermark,高水位,表示已经提交(commit)的最大日志偏移量,Kafka 中某条日志“已提交”的意思是 ISR 中所有节点都包含了此条日志,并且消费者只能消费 HW 之前的数据;
  • LEO: Log End Offset,表示当前 log 文件中下一条待写入消息的 offset;

        如上图所示,它代表一个日志文件,这个日志文件中有8条消息,0至5之间的消息为已提交消息,5至7的消息为未提交消息。日志文件的 HW 为5,表示消费者只能拉取到5之前的消息,而 offset 为5的消息对消费者而言是不可见的。日志文件的 LEO 为8,下一条消息将在此处写入。

        注意:所有副本都有对应的 HW 和 LEO,只不过 Leader 副本比较特殊,Kafka 使用 Leader 副本的高水位来定义所在分区的高水位。换句话说,分区的高水位就是其 Leader 副本的高水位。Leader 副本和 Follower 副本的 HW 有如下特点:

        Leader HW:min(所有副本 LEO),为此 Leader 副本不仅要保存自己的 HW 和 LEO,还要保存 follower 副本的 HW 和 LEO,而 follower 副本只需保存自己的 HW 和 LEO;
Follower HW:min(follower 自身 LEO,leader HW)。

四.Kafka为什么这么快

        Kafka 高性能的核心是保障系统低延迟、高吞吐地处理消息,为此,Kafaka 采用了许多精妙的设计:

  • 异步发送。
  • 批量发送。
  • 压缩技术。
  • PageCache 机制&顺序追加落盘。
  • 零拷贝。
  • 稀疏索引。
  • broker & 数据分区。
  • 多 reactor 多线程网络模型。

异步发送

        如上文所述,Kafka 提供了异步和同步两种消息发送方式。在异步发送中,整个流程都是异步的。调用异步发送方法后,消息会被写入 channel,然后立即返回成功。Dispatcher 协程会从 channel 轮询消息,将其发送到 Broker,同时会有另一个异步协程负责处理 Broker 返回的结果。同步发送本质上也是异步的,但是在处理结果时,同步发送通过 waitGroup 将异步操作转换为同步。使用异步发送可以最大化提高消息发送的吞吐能力。

批量发送

        Kafka 支持批量发送消息,将多个消息打包成一个批次进行发送,从而减少网络传输的开销,提高网络传输的效率和吞吐量。Kafka 的批量发送消息是通过以下两个参数来控制的:

        batch.size:控制批量发送消息的大小,默认值为16KB,可适当增加 batch.size 参数值提升吞吐。但是,需要注意的是,如果批量发送的大小设置得过大,可能会导致消息发送的延迟增加,因此需要根据实际情况进行调整。​
        linger.ms:控制消息在批量发送前的等待时间,默认值为0。当 linger.ms 大于0时,如果有消息发送,Kafka 会等待指定的时间,如果等待时间到达或者批量大小达到 batch.size,就会将消息打包成一个批次进行发送。可适当增加 linger.ms 参数值提升吞吐,比如10~100。

 压缩技术

        Kafka 支持压缩技术,通过将消息进行压缩后再进行传输,从而减少网络传输的开销(压缩和解压缩的过程会消耗一定的 CPU 资源,因此需要根据实际情况进行调整。),提高网络传输的效率和吞吐量。

        Kafka 支持多种压缩算法,通过配置参数 compression.type(默认值为 none,表示不进行压缩)控制。在 Kafka 2.1.0版本之前,仅支持 GZIP,Snappy 和 LZ4,2.1.0后还支持 Zstandard 算法(Facebook 开源,能够提供超高压缩比)。这些压缩算法性能对比(两指标都是越高越好)如下:

        吞吐量:LZ4>Snappy>zstd 和 GZIP
        压缩比:zstd>LZ4>GZIP>Snappy。


        在 Kafka 的生产者客户端中,当发送消息时,如果启用了压缩技术,Kafka 会将消息进行压缩后再进行传输。在消费者客户端中,如果消息进行了压缩,Kafka 会在消费消息时将其解压缩。注意:Broker 如果设置了和生产者不通的压缩算法,接收消息后会解压后重新压缩保存。Broker 如果存在消息版本兼容也会触发解压后再压缩。

PageCache 机制&顺序追加落盘

        kafka 为了提升系统吞吐、降低时延,Broker 接收到消息后只是将数据写入 PageCache 后便认为消息已写入成功,而 PageCache 中的数据通过 linux 的 flusher 程序进行异步刷盘(避免了同步刷盘的巨大系统开销),将数据顺序追加写到磁盘日志文件中。由于 PageCache 是在内存中进行缓存,因此读写速度非常快,可以大大提高读写效率。顺序追加写充分利用顺序 I/O 写操作,避免了缓慢的随机 I/O 操作,可有效提升 Kafka 吞吐。

零拷贝

        Kafka 中存在大量的网络数据持久化到磁盘(Producer 到 Broker)和磁盘文件通过网络发送(Broker 到 Consumer)的过程,这一过程的性能直接影响 Kafka 的整体吞吐量。传统的 IO 操作存在多次数据拷贝和上下文切换,性能比较低。Kafka 利用零拷贝技术提升上述过程性能,其中网络数据持久化磁盘主要用mmap技术,网络数据传输环节主要使用 sendfile 技术。

稀疏索引

        为了方便对日志进行检索和过期清理,kafka 日志文件除了有用于存储日志的 .log 文件,还有一个位移索引文件 .index 和一个时间戳索引文件 .timeindex 文件,并且三文件的名字完全相同,如下:

        Kafka 的索引文件是按照稀疏索引的思想进行设计的。稀疏索引的核心是不会为每个记录都保存索引,而是写入一定的记录之后才会增加一个索引值,具体这个间隔有多大则通过 log.index.interval.bytes 参数进行控制,默认大小为 4 KB,意味着 Kafka 至少写入 4KB 消息数据之后,才会在索引文件中增加一个索引项。可见,单条消息大小会影响 Kakfa 索引的插入频率,因此 log.index.interval.bytes 也是 Kafka 调优一个重要参数值。由于索引文件也是按照消息的顺序性进行增加索引项的,因此 Kafka 可以利用二分查找算法来搜索目标索引项,把时间复杂度降到了 O(lgN),大大减少了查找的时间。

位移索引文件的索引项结构如下:

相对位移:保存于索引文件名字上面的起始位移的差值,假设一个索引文件为:00000000000000000100.index,那么起始位移值即 100,当存储位移为 150 的消息索引时,在索引文件中的相对位移则为 150 - 100 = 50,这么做的好处是使用 4 字节保存位移即可,可以节省非常多的磁盘空间。

文件物理位置:消息在 log 文件中保存的位置,也就是说 Kafka 可根据消息位移,通过位移索引文件快速找到消息在 log 文件中的物理位置,有了该物理位置的值,我们就可以快速地从 log 文件中找到对应的消息了。下面我用图来表示 Kafka 是如何快速检索消息:

        假设 Kafka 需要找出位移为 3550 的消息,那么 Kafka 首先会使用二分查找算法找到小于 3550 的最大索引项:[3528, 2310272],得到索引项之后,Kafka 会根据该索引项的文件物理位置在 log 文件中从位置 2310272 开始顺序查找,直至找到位移为 3550 的消息记录为止。

broker & 数据分区

        Kafka 集群包含多个 broker。一个 topic 下通常有多个 partition,partition 分布在不同的 Broker 上,用于存储 topic 的消息,这使 Kafka 可以在多台机器上处理、存储消息,给 kafka 提供给了并行的消息处理能力和横向扩容能力。

多 reactor 多线程网络模型

        多 Reactor 多线程网络模型 是一种高效的网络通信模型,它采用了多个 Reactor 线程和多个工作线程来处理网络请求,可以充分利用多核 CPU 的性能,提高系统的吞吐量和响应速度。Kafka 为了提升系统的吞吐,在 Broker 端处理消息时采用了该模型,示意如下:

SocketServer 和 KafkaRequestHandlerPool 是其中最重要的两个组件:

  • SocketServer:实现 Reactor 模式,用于处理多个 Client(包括客户端和其他 broker 节点)的并发请求,并将处理结果返回给 Client。
  • KafkaRequestHandlerPool:Reactor 模式中的 Worker 线程池,里面定义了多个工作线程,用于处理实际的 I/O 请求逻辑。

整个服务端处理请求的流程大致分为以下几个步骤:

  1. Acceptor 接收客户端发来的请求。
  2. 轮询分发给 Processor 线程处理。
  3. Processor 将请求封装成 Request 对象,放到 RequestQueue 队列。
  4. KafkaRequestHandlerPool 分配工作线程,处理 RequestQueue 中的请求。
  5. KafkaRequestHandler 线程处理完请求后,将响应 Response 返回给 Processor 线程。
  6. Processor 线程将响应返回给客户端。

以时间戳查询消息

        Kafka 在0.10.1.1 版本增加了时间戳索引文件,因此我们除了直接根据偏移量索引文件查询消息之外,还可以根据时间戳来访问消息。consumer-API 提供了一个offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch)方法,该方法入参为一个Map 对象,Key 为待查询的分区,Value 为待查询的时间戳,该方法会返回时间戳大于等于待查询时间的第一条消息对应的偏移量和时间戳。需要注意的是,若待查询的分区不存在,则该方法会被一直阻塞。

五.Kafka写入消息流程

  1.  连接到 zk 集群,从 zookeeper 中拿到对应的 topic 的 partition 信息和 partition 的 leader 的相关信息(注意:kafka0.8 版本以后,producer 写入数据不依赖 zookpeer !)
  2. 连接到对应的 leader 对应的 broker
  3. producer 将消息发送到 partition 的leader上
  4. leader 将消息写入本地 log, follower 从 leader pull 同步消息
  5. 写入本地 log 后,依次向 leader 返回/发送 ack
  6. leader 收到所有 replication 的 ack 后,向 producer 发送 ack
  7. 所有的 ISR 中的数据写入完成,才完成提交,整个写过程结束

六.Kafka读取消息流程

  1. 连接到 zk 集群,从zookeeper 中拿到对应的 topic 的 partition 信息和 partition 的 leader 的相关信息
  2. 连接到对应的 leader 对应的 broker
  3. consumer 将自己保存的 offset 发送给 leader
  4. leader 根据 offset 等信息定位到 segment(索引文件 .index 和日志文件 .log )
  5. 根据索引文件中的内容,定位到日志文件中该偏移量对应的开始位置读取相应长度的数据并返回给 consumer

说明:.index文件 存储大量的索引信息,.log文件存储大量的数据,索引文件中的元数据指向对应数据文件中 message 的物理偏移地址。

举例:如现在要查找偏移量 offset 为 3 的消息,根据 .index 文件命名我们可以知道,offset 为 3 的索引应该从00000000000000000000.index 里查找。根据上图所示,其对应的索引地址为 756-911,所以 Kafka 将读取00000000000000000000.log 756~911区间的数据。

        index 和 log 文件以当前 segment 的第一条消息的 offset 命名。偏移量 offset 是一个 64 位的长整形数,固定是20 位数字,长度未达到,用 0 进行填补,索引文件和日志文件都由此作为文件名命名规则。所以从上图可以看出,该分区的偏移量是从 57819368 开始的,.index 和 .log 文件名称都为 00000000000057819368。

七.幂等性和事务

幂等性

        Kafka 通过 幂等生产者(Idempotent Producer)来实现幂等性。幂等生产者的配置参数是 enable.idempotence=true,启用后,Kafka 会为每个生产者分配一个唯一的 生产者 ID(Producer ID, PID),并为每条消息分配一个 序列号(Sequence Number)。Kafka broker 使用这些信息来检测和丢弃重复的消息。

生产者 ID (PID):当生产者第一次连接到 Kafka broker 时,broker 会为其分配一个唯一的 PID。这个 PID 在生产者的生命周期内保持不变,即使生产者断开连接并重新连接,它仍然会使用相同的 PID。

序列号 (Sequence Number):每个生产者为每个分区维护一个递增的序列号。每次生产者发送一条消息时,序列号会递增,并与消息一起发送给 Kafka broker。Kafka broker 使用 PID 和序列号来跟踪每个生产者发送的消息。

重复消息检测

消息去重:当 Kafka broker 收到一条消息时,它会检查该消息的 PID 和序列号。如果 broker 发现已经收到了相同 PID 和序列号的消息,它会认为这是一条重复消息,并将其丢弃。否则,broker 会将消息写入日志,并更新序列号。

超时机制:为了防止生产者长时间未发送消息导致序列号过期,Kafka 引入了 会话超时(Session Timeout)机制。如果生产者在超时时间内没有发送任何消息,Kafka 会认为该生产者的会话已结束,并重新分配新的 PID 和序列号。默认的会话超时时间为 60 秒。

幂等性的优势

  • 避免重复消息:幂等性确保即使生产者重试发送消息,也不会导致重复消息的产生。这对于需要严格消息顺序的应用场景非常重要,例如金融交易系统、订单处理系统等。
  • 简化重试逻辑:由于 Kafka 自动处理了重复消息的检测和去重,生产者不再需要手动实现复杂的重试逻辑,简化了开发工作。
  • 提高可靠性:幂等性提高了消息传递的可靠性,特别是在网络不稳定或生产者故障的情况下,确保了消息的完整性和一致性。

事务

        事务支持是指 Kafka 提供了一种机制,允许多个操作作为一个整体进行提交或回滚,确保这些操作要么全部成功,要么全部失败。Kafka 的事务支持主要用于实现 精确一次语义(Exactly-Once Semantics, EOS),确保消息在生产、消费和处理过程中不会丢失或重复。

        Kafka 的事务支持不仅适用于生产者发送消息的操作,还支持跨多个主题和分区的事务性操作。具体来说,Kafka 事务可以包括以下几种操作:

  • 消息生产:生产者可以将多条消息作为同一个事务的一部分发送到不同的主题和分区。
  • 消息消费:消费者可以将多个消息的偏移量提交作为同一个事务的一部分,确保这些消息的消费是原子性的。
  • 流处理:Kafka Streams API 支持事务性操作,允许开发者在流处理过程中保证数据的一致性和完整性。

        Kafka 的事务支持基于 两阶段提交协议(Two-Phase Commit Protocol),确保事务中的所有操作要么全部成功,要么全部失败。以下是 Kafka 事务的典型工作流程:

初始化事务
  • 生产者调用 initTransactions() 方法,初始化一个事务上下文。Kafka 为该事务分配一个唯一的 事务 ID(Transaction ID),并记录事务的开始时间。

添加操作到事务
  • 生产者可以通过 send() 方法将消息添加到事务中。这些消息会被暂存起来,直到事务提交为止。

  • 生产者还可以通过 addOffsetsToTransaction() 方法将消费者的偏移量提交操作添加到事务中,确保消息的消费和处理是原子性的。

提交或回滚事务
  • 当所有操作完成后,生产者可以调用 commitTransaction() 方法提交事务。Kafka 会确保事务中的所有操作都成功完成,并将消息写入日志。
  • 如果某个操作失败,生产者可以调用 abortTransaction() 方法回滚事务,确保事务中的所有操作都被取消。
 事务协调器
  • Kafka 为每个事务分配了一个 事务协调器(Transaction Coordinator),负责管理事务的状态和协调多个 broker 之间的同步。事务协调器会跟踪事务的进度,并在适当的时候通知其他 broker 提交或回滚事务。

Kafka事务的隔离级别
  • 读已提交(Read Committed):消费者只能读取已经被提交的消息,不能读取正在处理中的事务消息。这是 Kafka 默认的隔离级别,适用于大多数场景。
  • 读未提交(Read Uncommitted):消费者可以读取尚未提交的事务消息。这种隔离级别适用于对一致性要求较低的场景,但可能会导致消费者读取到未提交的消息。
事务的优势

        精确一次语义:通过事务支持,Kafka 可以实现 精确一次语义,确保消息在生产、消费和处理过程中不会丢失或重复。这对于需要强一致性的应用场景非常重要,例如金融交易系统、订单处理系统等。

        跨主题和分区的原子性:Kafka 的事务支持允许多个操作跨越多个主题和分区,确保这些操作要么全部成功,要么全部失败。这种方式提供了更高的灵活性和可靠性。

        流处理的一致性:Kafka Streams API 支持事务性操作,允许开发者在流处理过程中保证数据的一致性和完整性。这对于构建复杂的实时数据处理管道非常有用。

事务的配置

要启用 Kafka 的事务支持,生产者需要配置以下参数:

enable.idempotence=true  # 启用幂等性
transactional.id=<unique_transaction_id>  # 设置唯一的事务 ID


此外,Kafka 还提供了一些与事务相关的配置参数,用于控制事务的超时和隔离级别:

transaction.timeout.ms:指定事务的最大持续时间。如果事务在超时时间内未完成,Kafka 会自动回滚该事务。默认值为 60000 毫秒(60 秒)。

transaction.state.log.replication.factor:指定事务状态日志的副本数。默认值为 3,建议根据集群的规模和可靠性需求进行调整。

isolation.level=read_committed:指定消费者的隔离级别为“读已提交”,确保消费者只能读取已经被提交的消息。

八.如何保证kafka消息的顺序性

方法1:使用Partition参数

当你创建一个ProducerRecord对象时,可以指定分区。例如:

String topic = "your_topic";
int partition = 1; // 指定分区编号
String key = "your_key";
String value = "your_value";
 
ProducerRecord<String, String> record = new ProducerRecord<>(topic, partition, key, value);
producer.send(record);

方法2:使用Key参数

没有指明partition但是指明了key的情况下,将key的hash值与partition数量取模得到写入的partition,相同key的消息会写到同一个partition保证顺序性。

方法3:使用Partitioner接口自定义分区策略

如果你想要基于特定的逻辑(例如,基于键的哈希值)来决定消息应该发送到哪个分区,你可以实现org.apache.kafka.clients.producer.Partitioner接口。然后,在创建生产者时,通过配置partitioner.class属性来指定你的自定义分区器。

public class CustomPartitioner implements Partitioner {
    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        // 例如,基于键的哈希值来选择分区
        return Math.abs(keyBytes.hashCode() % numPartitions);
    }
 
    @Override
    public void close() {
        // 关闭资源
    }
 
    @Override
    public void configure(Map<String, ?> configs) {
        // 配置初始化
    }
}


Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("partitioner.class", "your.package.CustomPartitioner"); // 指定你的分区器类
 
KafkaProducer<String, String> producer = new KafkaProducer<>(props);

参考文章

图解Kafka:架构设计、消息可靠、数据持久、高性能背后的底层原理_kafka底层原理-优快云博客

【kafka基础】-- 读写流程及举例_kafka读写流程-优快云博客

Kafka优势剖析-幂等性和事务_kafka幂等性-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值