文章目录
一、简介
Kafka 是一个 分布式 的基于 发布/订阅 模式的 消息队列(Message Queue),主要应用于大数据实时处理领域。
- 消息队列的两种模式
- 点对点模式 一对一 ,消费者主动拉取数据,消息收到后消息清除
- 发布 /订阅模式 一对多 ,消费者消费数据之后不会清除消息
- 基础架构
- Producer :消息生产者,就是向kafka broker 发消息的客户端;
- Consumer :消息消费者,向kafka broker 取消息的客户端;
- Consumer Group (CG):消费者组,由多个consumer 组成。消费者组内每个消费者负
责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所
有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。 - Broker :一台kafka 服务器就是一个broker。一个集群由多个broker 组成。一个broker
可以容纳多个topic。 - Topic :可以理解为一个队列,生产者和消费者面向的都是一个topic;
- Partition:为了实现扩展性,一个非常大的topic 可以分布到多个broker(即服务器)上,
一个topic 可以分为多个partition,每个partition 是一个有序的队列; - Replica:副本,为保证集群中的某个节点发生故障时,该节点上的partition 数据不丢失,且
kafka仍然能够继续工作 kafka提供了副本机制,一个 topic的每个分区都有若干个副本,一个 leader和若干个 follower。 - leader 每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对
象都是 leader。 - follower 每个分区多个副本中的“从”,实时从 leader中同步数据,保持和 leader数据
的同步。 leader发生故障时,某个 follower会成为新的 follower。
二、部署使用
2.1 集群部署
- 准备软件包
[omm@bigdata01 soft]# tar -zxf kafka_2.13-2.7.0.tgz -C /opt/module/
[omm@bigdata01 soft]# cd /opt/module/
[omm@bigdata01 module]# ln -s kafka_2.13-2.7.0 kafka
- 修改配置文件
[omm@bigdata01 config]# pwd
/opt/module/kafka/config
[omm@bigdata01 config]# cp server.properties{,.bak}
[omm@bigdata01 config]# grep -Ev '^$|^#' server.properties.bak > server.properties
[omm@bigdata01 config]# vi server.properties
[omm@bigdata01 config]# cat server.properties
broker.id=0
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/opt/module/kafka/kafka-logs
num.partitions=1
num.recovery.threads.per.data.dir=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
zookeeper.connect=bigdata01:2181,bigdata02:2181,bigdata03:2181
zookeeper.connection.timeout.ms=18000
group.initial.rebalance.delay.ms=0
[omm@bigdata01 config]#
- 分发及修改
broker.id
[omm@bigdata01 module]$ xsync kafka
[omm@bigdata02 config]$ pwd
/opt/module/kafka/config
[omm@bigdata02 config]$ grep broker server.properties
broker.id=1
[omm@bigdata03 config]$ grep broker server.properties
broker.id=2
[omm@bigdata03 config]$
- 群起脚本
#!/bin/bash
for host in bigdata01 bigdata02 bigdata03
do
echo ""
echo "---- $host kafka $1 ----"
echo ""
case $1 in
"start")
ssh $host "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
;;
"stop")
ssh $host "/opt/module/kafka/bin/kafka-server-stop.sh /opt/module/kafka/config/server.properties"
;;
esac
done
echo ""
2.2 topic 命令
- 创建 topic
--partitions:分区数最好设置为
broker-num * data-dir-per-broker * N (e.g. 3*1*N = 3N)
--replication-factor <= broker-num
[omm@bigdata01 bin]$ pwd
/opt/module/kafka/bin
[omm@bigdata01 bin]$ ./kafka-topics.sh --zookeeper bigdata01:2181 --create --partitions 3 --replication-factor 2 --topic first
Created topic first.
- 列出 topic
[omm@bigdata01 bin]$ ./kafka-topics.sh --zookeeper bigdata01:2181 --list
first
- topic 详情
[omm@bigdata01 bin]$ ./kafka-topics.sh --zookeeper bigdata01:2181 --describe --topic first
Topic: first PartitionCount: 3 ReplicationFactor: 2 Configs:
Topic: first Partition: 0 Leader: 1 Replicas: 1,2 Isr: 1,2
Topic: first Partition: 1 Leader: 2 Replicas: 2,0 Isr: 2,0
Topic: first Partition: 2 Leader: 0 Replicas: 0,1 Isr: 0,1
- 修改 topic 分区数
[omm@bigdata01 bin]$ ./kafka-topics.sh --zookeeper bigdata01:2181 --alter --topic first --partitions 6
WARNING: If partitions are increased for a topic that has a key, the partition logic or ordering of the messages will be affected
Adding partitions succeeded!
- 查看 topic 分区在磁盘目录中的分布情况
[omm@bigdata01 kafka-logs]$ pwd
/opt/module/kafka/kafka-logs
[omm@bigdata01 kafka-logs]$ ll | grep first
drwxrwxr-x 2 omm omm 141 Feb 18 14:53 first-1
drwxrwxr-x 2 omm omm 141 Feb 18 14:53 first-2
drwxrwxr-x 2 omm omm 141 Feb 18 14:55 first-4
drwxrwxr-x 2 omm omm 141 Feb 18 14:55 first-5
[omm@bigdata02 kafka-logs]$ ll | grep first
drwxrwxr-x 2 omm omm 141 Feb 18 14:53 first-0
drwxrwxr-x 2 omm omm 141 Feb 18 14:53 first-2
drwxrwxr-x 2 omm omm 141 Feb 18 14:55 first-3
drwxrwxr-x 2 omm omm 141 Feb 18 14:55 first-5
[omm@bigdata03 kafka-logs]$ ll | grep first
drwxrwxr-x 2 omm omm 141 Feb 18 14:53 first-0
drwxrwxr-x 2 omm omm 141 Feb 18 14:53 first-1
drwxrwxr-x 2 omm omm 141 Feb 18 14:55 first-3
drwxrwxr-x 2 omm omm 141 Feb 18 14:55 first-4
[omm@bigdata01 bin]$ ./kafka-topics.sh --zookeeper bigdata01:2181 --describe --topic first
Topic: first PartitionCount: 6 ReplicationFactor: 2 Configs:
Topic: first Partition: 0 Leader: 1 Replicas: 1,2 Isr: 1,2
Topic: first Partition: 1 Leader: 2 Replicas: 2,0 Isr: 2,0
Topic: first Partition: 2 Leader: 0 Replicas: 0,1 Isr: 0,1
Topic: first Partition: 3 Leader: 1 Replicas: 1,2 Isr: 1,2
Topic: first Partition: 4 Leader: 2 Replicas: 2,0 Isr: 2,0
Topic: first Partition: 5 Leader: 0 Replicas: 0,1 Isr: 0,1
- 删除 topic
[omm@bigdata01 bin]$ ./kafka-topics.sh --zookeeper bigdata01:2181 --delete --topic first
Topic first is marked for deletion.
Note: This will have no impact if delete.topic.enable is not set to true.
2.3 message 命令
- 生产消息
--broker-list
:指定了所使用的 broker 列表
[omm@bigdata01 bin]$ ./kafka-console-producer.sh --broker-list bigdata01:9092 --topic first
>hello
>how are you doing?
>
- 消费消息
- 对于消费者,kafka中有两个设置的地方:对于老的消费者,由--zookeeper(已废弃)参数设置;对于新的消费者,由--bootstrap-server参数设置
- 如果使用了--zookeeper参数,那么consumer的信息将会存放在zookeeper中
/consumers/[group_id]/offsets/[topic]/[broker_id-part_id]
,这个是查看某个group_id的某个topic的offset - 如果使用了--bootstrap-server参数,那么consumer的信息将会存放在kafka之中
[omm@bigdata02 bin]$ ./kafka-console-consumer.sh --bootstrap-server bigdata02:9092 --topic first
hello
how are you doing?
- 从头消费消息
[omm@bigdata03 bin]$ ./kafka-console-consumer.sh --bootstrap-server bigdata02:9092 --topic first
^CProcessed a total of 0 messages
[omm@bigdata03 bin]$ ./kafka-console-consumer.sh --bootstrap-server bigdata02:9092 --from-beginning --topic first
how are you doing?
hello
三、深入架构
3.1 文件存储机制
- 落地文件
- topic 是逻辑上的概念,而partition 是物理上的概念,每个partition 对应于一个log 文
件,该log 文件中存储的就是producer 生产的数据。 - Producer 生产的数据会被不断追加到该log 文件末端,且每条数据都有自己的offset。
- 消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。
- 消息读取规则
- index 和log 文件以当前segment 的第一条消息的offset 命名。
- .index文件存储大量的索引信息 ,.log文件存储大量的数据 ,索引文件中的元数据指向对应数据文件中 message的物理偏移地址 。
3.2 生产者
3.2.1 分区策略
- 分区的原因
- 方便在集群中扩展 ,每个 Partition可以通过调整以适应它所在的机器,而一个 topic又可以有多个 Partition组成,因此整个集群就可以适应任意大小的数据了;
- 可以提高并发 ,因为可以以 Partition为单位读写了。
- 分区的原则
我们需要将 producer 发送的数据封装成一个 ProducerRecord对象 。
- 指明 partition 的情况下,直接将指明的值直接作为 partiton 值;
- 没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值;
- 既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition 值,也就是常说的 round-robin 算法。
3.2.2 数据可靠性保证
为保证 producer 发送的数据能可靠的发送到指定的 topic,topic的每个 partition收到producer发送的数据后, 都需要向 producer发送 ack。
- 副本数据同步策略
方案 | 优点 | 缺点 |
---|---|---|
半数以上完成同步,就发送ack | 延迟低 | 选举新的leader 时,容忍n 台节点的故障,需要2n+1 个副本 |
全部完成同步,才发送ack | 选举新的leader 时,容忍n 台节点的故障,需要n+1 个副本 | 延迟高 |
Kafka 选择了第二种方案,原因如下:
- 同样为了容忍n 台节点的故障,第一种方案需要2n+1 个副本,而第二种方案只需要n+1个副本,而Kafka 的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
- 虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka 的影响较小。
- ISR
- Leader 维护了一个动态的in-sync replica set (ISR),意为和leader 保持同步的follower 集合。
- 当ISR 中的follower 完成数据的同步之后,leader 就会给follower 发送ack。
- 如果follower长时间未向leader 同步数据, 则该follower 将被踢出ISR , 该时间阈值由replica.lag.time.max.ms 参数设定。
- Leader 发生故障之后,就会从ISR 中选举新的leader。
- acks
- 0:producer 不等待broker 的ack,这一操作提供了一个最低的延迟,broker 一接收到还
没有写入磁盘就已经返回,当broker 故障时有可能丢失数据; - 1:producer 等待broker 的ack,partition 的leader 落盘成功后返回ack,如果在follower同步成功之前leader 故障,那么将会丢失数据;
- -1(all):producer 等待broker 的ack,partition 的leader 和follower 全部落盘成功后才返回ack。但是如果在follower 同步完成后,broker 发送ack 之前,leader 发生故障,那么会造成数据重复。
- 故障处理细节
故障处理只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。
-
follower 故障:follower 发生故障后会被临时踢出ISR,待该follower 恢复后,follower 会读取本地磁盘记录的上次的HW,并将log 文件高于HW 的部分截取掉,从HW 开始向leader 进行同步。
-
leader 故障:leader 发生故障之后,会从ISR 中选出一个新的leader,之后其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
3.2.3 Exactly Once语义
- At Least/Most Once
- 将服务器的ACK级别设置为 -1,可以保证 Producer到 Server之间不会丢失数据,即 At Least Once语义 。
- 相对的,将服务器 ACK级别设置为 0,可以保证生产者每条消息只会被发送一次,即 At Most Once语义。
- 问题引出
- At Least Once可以保证数据不丢失,但是不能保证数据不重复;相对的, At Least Once可以保证数据不重复,但是不能保证数据不丢失。
- 对于一些非常重要的信息,比如说交易数据,下游数据消费者要求数据既不重复也不丢失,即 Exactly Once语义。
- 在 0.11版本以前的 Kafka,对此是无能为力的,只能保证数据不丢失,再在下游消费者对数据做全局去重。
- 0.11版本的 Kafka,引入了一项重大特性:幂等性。所谓的幂等性就是指 Producer不论
向 Server发送多少次重复数据, Server端都只会持久化一条。幂等性结合 At Least Once语义,就构成了 Kafka的 Exactly Once语义。
- Exactly Once
要启用幂等性,只需要将Producer的参数中 enable.idompotence设置为 true即可。
- Kafka的幂等性实现其实就是将原来下游需要做的去重放在了数据上游。
- 开启幂等性的 Producer在初始化的时候会被分配一个 PID,发往同一 Partition的消息会附带 Sequence Number。
- 而Broker端会对 <PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时, Broker只会持久化一条。
- 但是Producer重启/重连PID就会变化,同时不同的 Partition也具有不同主键,所以幂等性 无法保证跨分区跨会话 的 Exactly Once。
3.3 消费者
- 消费方式
- consumer采用 pull 模式从 broker中读取数据。
- push 的目标是尽可能以最快速度传递消息,这样很容易造成 consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。
- pull 模式不足之处是,如果kafka 没有数据,消费者可能会陷入循环中,一直返回空数据。
- 针对这一点,Kafka 的消费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer 会等待一段时间之后再返回,这段时长即为timeout。
- 分区分配策略
一个consumer group 中有多个consumer,一个 topic 有多个partition,所以必然会涉及到partition 的分配问题,即确定那个partition 由哪个consumer 来消费。
- RoundRobin
此策略将消费者组订阅的 Topics作为整体 进行轮询,假如消费者组订阅的是多个不同的 Topic,会产生消息消费错误的问题。
- Range(默认)
此策略将 单个Topic 尽量均分到消费者组中订阅的Cousumers。
- offsets 的维护
consumer 需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。
Kafka 0.9 版本之前,consumer 默认将offset 保存在Zookeeper 中;从0.9 版本开始,consumer 默认将offset 保存在Kafka 一个内置的topic 中,该topic 为__consumer_offsets。
3.4 高效读写
- 顺序写磁盘
- Kafka的 producer生产数据,要写入到 log文件中,写的过程是一直追加到文件末端,为顺序写 。
- 官网有数据表明,同样的磁盘,顺序写能到 600M/s,而随机写只有 100K/s。
- 这与磁盘的机械机构有关,顺序写之所以快,是因为其省去了 大量 磁头寻址的时间 。
- 零复制技术
3.5 Zookeeper 在Kafka 中的作用
Kafka 集群中有一个broker 会被选举为Controller,负责管理集群broker 的上下线,所有topic 的分区副本分配和leader 选举等工作。
[zk: localhost:2181(CONNECTED) 16] get -s /controller
{"version":1,"brokerid":0,"timestamp":"1613693826004"}
cZxid = 0x30000005f
ctime = Fri Feb 19 08:17:06 CST 2021
mZxid = 0x30000005f
mtime = Fri Feb 19 08:17:06 CST 2021
pZxid = 0x30000005f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x100000cd0d10000
dataLength = 54
numChildren = 0
[zk: localhost:2181(CONNECTED) 17]
Controller 的管理工作都是依赖于Zookeeper 的。
- partition 的 leader 选举过程
3.6 事务
Kafka 从0.11 版本开始引入了事务支持。事务可以保证Kafka 在Exactly Once 语义的基础上,生产和消费可以跨分区和会话,要么全部成功,要么全部失败。
- Producer事务
Transaction ID 结合 PID (幂等性) 保证了生产者跨会话跨分区的 Exactly Once。【适用于特殊场景】
- 为了实现跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将 Producer获得的 PID和 Transaction ID绑定。
- 当 Producer重启后就可以通过正在进行的 Transaction ID获得原来的 PID。
- 为了管理Transaction Kafka引入了一个新的组件 Transaction Coordinator。
- Producer就是通过和 Transaction Coordinator交互获得 Transaction ID对应的任务状态。
- Transaction Coordinator还负责将事务所有写入 Kafka的一个内部 Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。
- Consumer事务
- 上述事务机制主要是从Producer方面考虑,对于 Consumer而言,事务的保证就会相对较弱,尤其时无法保证 Commit的信息被精确消费。
- 这是由于 Consumer可以通过 offset访问任意信息,而且不同的 Segment File生命周期不同,同一事务的消息可能会出现重启后被删除的情况。
四、Kafka API
4.1 生产者
4.1.1 消息发送流程
- Kafka的 Producer发送消息采用的是 异步发送 的方式。
- 在消息发送的过程中,涉及到了两个线程 main线程和 Sender线程 ,以及 一个线程共享变量 RecordAccumulator。
- main线程将消息发送给 RecordAccumulator Sender线程不断从RecordAccumulator中拉取消息发送到 Kafka broker。
batch.size
:只有数据积累到batch.size 之后,sender 才会发送数据。
linger.ms
:如果数据迟迟未达到batch.size,sender 等待linger.time 之后就会发送数据。
4.1.2 KafkaProducer
- 引入依赖
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
- 编写实现类
可配置项及默认值: http://kafka.apache.org/26/documentation.html#producerconfigs
package com.simwor.kafka;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;
public class KafkaProducerTest {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "bigdata01:9092,bigdata02:9092,bigdata03:9092");
props.put(ProducerConfig.ACKS_CONFIG, "all");
props.put(ProducerConfig.RETRIES_CONFIG, 1);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);
for (int i = 0; i < 10; i++)
producer.send(new ProducerRecord<>("first", Integer.toString(i)));
producer.close();
}
}
- 运行验证
[omm@bigdata01 bin]$ ./kafka-console-consumer.sh --bootstrap-server bigdata02:9092 --topic first
0
1
2
3
4
5
6
7
8
9
4.2 消费者
package com.simwor.kafka;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.time.Duration;
import java.util.Arrays;
import java.util.Properties;
public class KafkaConsumerTest {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "bigdata01:9092,bigdata02:9092,bigdata03:9092");
props.put("group.id", "test");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("first"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> record : records)
System.out.printf("offset = %d, key = %s, value= %s%n", record.offset(), record.key(), record.value());
}
}
}
4.3 拦截器
4.3.1 原理
- 简介
- Producer拦截器 (interceptor)主要用于实现 clients端的定制化控制逻辑。
- 对于producer而言, interceptor使得用户在消息发送前以及 producer回调逻辑前有机会对消息做一些定制化需求,比如 修改消息 等。
- 同时, producer允许用户指定多个 interceptor按序作用于同一条消息从而形成一个拦截链 (interceptor chain)。
- Intercetpor的实现接口是
org.apache.kafka.clients.producer.ProducerInterceptor
。
- 接口方法
- configure(configs):获取配置信息 和 初始化数据时调用 。
- onSend(ProducerRecord):该方法封装进KafkaProducer.send方法中,即它运行在用户主线程中。Producer确保在消息被序列化以及计算分区前调用该方法。用户可以在该方法中对消息做任何操作,但最好保证不要修改消息所属的 topic和分区, 否则会影响目标分区的计算 。
- onAcknowledgement(RecordMetadata, Exception):该方法会在消息从 RecordAccumulator成功 发送到 Kafka Broker之后,或者在发送过程中失败时调用。 并且通常都是在 producer回调逻辑触发之前。 onAcknowledgement运行在producer的 IO线程中,因此不要在该方法中放入很重的逻辑,否则会拖慢 producer的消息发送效率 。
- close:关闭interceptor,主要用于执行一些资源清理工作。如前所述,interceptor可能被运行在多个线程中,因此在具体实现时用户需要自行确保线程安全。另外 倘若指定了多个 interceptor,则 producer将按照指定顺序调用它们 ,并仅仅是捕获每个 interceptor可能抛出的异常记录到错误日志中而非在向上传递。这在使用过程中要特别留意。
4.3.2 自定义
- 代码
package com.simwor.kafka;
import org.apache.kafka.clients.producer.*;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
public class KafkaInterceptorTest implements ProducerInterceptor<String, String> {
public static void main(String[] args) {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "bigdata01:9092,bigdata02:9092,bigdata03:9092");
props.put(ProducerConfig.ACKS_CONFIG, "all");
props.put(ProducerConfig.RETRIES_CONFIG, 1);
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
//interceptors
props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, Collections.singletonList("com.simwor.kafka.KafkaInterceptorTest"));
Producer<String, String> producer = new KafkaProducer<>(props);
for (int i = 0; i < 10; i++)
producer.send(new ProducerRecord<>("first", Integer.toString(i)));
producer.close();
}
@Override
public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
return new ProducerRecord<>(record.topic(), record.partition(),
record.timestamp(), record.key(), System.currentTimeMillis() + ", " + record.value());
}
@Override public void onAcknowledgement(RecordMetadata metadata, Exception exception) { }
@Override public void close() { }
@Override public void configure(Map<String, ?> configs) { }
}
- 运行验证
[omm@bigdata01 ~]$ cd /opt/module/kafka/bin/
[omm@bigdata01 bin]$ ./kafka-console-consumer.sh --bootstrap-server bigdata01:9092 --topic first
1613871255570, 0
1613871256049, 1
1613871256049, 2
1613871256049, 3
1613871256049, 4
1613871256049, 5
1613871256050, 6
1613871256050, 7
1613871256050, 8
1613871256050, 9
五、总结
- Kafka 的幂等性保证了同分区但会话的ExactlyOnce;
- Kafka 的事务+幂等性保证了跨分区多会话的ExactlyOnce;
- Kafka 的LEO和HW保证副本之间的数据一致性;
- .index + .log 保证快速索引到消息且开始顺序读取消息。