二, Kafka 生产者知识点总结
文章目录
2.3 Kafka 生产者
先动动手学会这个噢: Kafka 生产者 API的快速上手
2.3.1 生产者对topic分区的原因
- 方便在集群中扩展,一个topic可以由多个partition组成, 每个partition可以通过调整以适应它所在的机器, , 因此整个集群就可以适应任意大小的数据了.
- 提高读写并行度,在分布式系统中, 同一个topic 的不同partition可以分布在多台主机中, 这就打破了单主机的IO和处理能力限制, 提高了读写的并行度.
- 便于数据复制备份, 提高数据冗余度和容灾能力
2.3.2 生产者消息分区策略
我们需要将producer发送的数据封装成一个ProducerRecord对象.
- 前五种方法, 即指明partition的情况下, 直接将指明的值直接作为partition值;
- 没有指明partition但有key的情况下, 将
key的hash值%topic的partition值
, 取余结果即为partition值; - 既没有指明partition值又没有key值的情况下, 第一次调用时随机生成一个整数(后面每次调用在这个整数上自增), 将这个
随机整数值%topic可用的partition总数
, 取余结果就是partition值, 这种是常说的round-robin算法.
Q:待解决问题: key是什么玩意?
代码示例:
[Kakfka 的各种分区策略的实现demo]()
2.3.3 生产经验–生产者如何提高数据吞吐量
四个参数, 缓冲区大小, buffer.memory, 批次大小, batch.size , 等待时间, linger.ms, 压缩, compression.type
- 关于该参数的详细介绍: 点我
代码示例:
package producer2;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
public class CustomerProducerWithCallBack {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1. 配置对象
Properties prop = new Properties();
//2. 设置参数
//2.1 设置bootStrap
prop.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "bigdata01:9092, bigdata02:9092");
//2.2 设置key和value 的序列化
prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//3.把配置传入到kafkaproducer对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(prop);
///
// 增大生产者吞吐量的四个参数
// 1. batch.size: 批次大小, 默认 16K
prop.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
// 2. linger.ms: 等待时间,默认 0
prop.put(ProducerConfig.LINGER_MS_CONFIG, 1);
// 3. RecordAccumulator: 缓冲区大小, 默认 32M: buffer.memory
prop.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
// 4. compression.type: 压缩,默认 none,可配置值 gzip、 snappy、lz4 和 zstd
prop.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"snappy");
//
//4.发送
kafkaProducer.send(new ProducerRecord<String, String>("first", "this is a string"),
new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
System.out.println("OffSet:" + recordMetadata.offset()
+ "Topic: " + recordMetadata.topic()
+ "Partition: " + recordMetadata.partition()
+ "string: " + recordMetadata);
}
}).get(); //同步方法
//5. 千万不要忘记关闭资源!!!
kafkaProducer.close();
}
}
2.3.3 生产经验–数据可靠性
保证
- 包括副本同步策略和ack机制
1. follower与leader数据同步的策略(副本同步策略)
- 为了保证Producer发送的数据, 能够可靠的发送到指定的topic, topic的每个partition收到producer发送的数据后, 都需要向producer发送ack, 如果producer收到ack, 就会进行下一轮的发送, 否则重新发送数据.
follower与leader数据同步的策略(副本同步策略)
方案 | 优点 | 缺点 |
---|---|---|
1.半数以上完成同步, 就发送ack | 延迟低 | 选举新的leader时, 容忍n台结点的故障, 需要设置2n+1个副本 |
2. 全部完成同步,才发送ack | 选举新的leader时, 容忍n台节点的故障, 只需要设置n+1个副本 | 延迟高 |
kafka选择了第2种策略, 原因是:
1, 同样是为了容忍n台节点的故障, 半数同步的方案需要2n+1个副本, 而Kafka的每个分区都有大量的数据, 毫无疑问这种方案会造成大量数据的冗余.
2, 虽然全部同步完成的方案延迟较高, 但是网络延迟对Kafka 的影响较小.
Q: 没看懂半数跟全同步需要副本数量的问题?
A:
- 首先啊, 我们要知道kafka是利用zookeeper的选举机制来确定集群中哪一台主机是leader的, 而zookeeper初次选举时采用的是半数以上选举机制(即只要集群中有半数以上的主机确定出了一个leader, 就算是选举完成啦); 然后呢为了能够确保选举出集群中主机们的leader, kafka集群中必须总是保证集群中正常工作的主机为总主机数量的半数以上, 这也跟zookeeper 的半数存活机制是一致的(仔细品品这句话).
- 这个时候, 我们就出现了同步副本和半数存活结合一起考虑的问题, 即当对集群进行同步时, 一定要确保半数以上的副本是正常的, 这样我们才能通过副本去恢复半数以上的主机, 使之能够能够正常的选举出集群的leader.
- 那么当采用半数同步策略时, 当n台节点发生故障时, 我们最低限度的需要
设置2n+1个副本数(注意是设置, 不是要求一定要有这些副本)
才能确保集群正常选举, 为啥呢? 可别忘了这2n+1采用这种了半数以上的同步机制, 这就使得在这些副本中肯定会有至少n+1个副本是正常的(这一点是必须能保证的), 这样才能可以正常恢复n+1个节点,正常选举出leader. - 当采用全同步策略时, 当n台节点发生故障时, 我们最低限度的需要
设置n+1个副本数
才能确保集群能够正常选举, 这n+1个节点因为采用了全同步策略(这一点也是保证了最低n+1个节点正常同步出副本), 所以就能够确保集群中还有n+1个主机能够正常的选举出leader.
补充:
- 半数存活机制;
- 两种策略需要设置的副本数为什么会有不同呢?
1.1 ISR(in-sync replica set)–>与leader保持同步的follower集合
- 当我们采用第二种方案(全同步才发送ack)后, leader收到数据, 所有的follower都开始同步数据直到全部同步完成,然而若是有个follower因为某种故障, 迟迟不能与leader同步, 该怎么解决呢?
Leader维护了一个动态的ISR(in-sync replica set), 意为
和leader保持同步的follower集合
.
- 当ISR中的follower完成数据的同步之后, leader就会给follower发送ack.
- 如果follower长时间未向leader同步数据, 则该follower就会被踢出ISR, 该事件阈值由
replica.lag.time.max.ms
参数设定- Leader发生故障之后, 就会从ISR中选举新的leader.
2. ack应答机制
- 对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等 ISR 中的 follower 全部接收成功。
- 所以 Kafka 为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。
acks 参数配置:
[acks = 0]:
- 当ack=0时, producer不等待broker 的ack, 这一操作提供了最低的延迟, producer在broker数据一接收到还没写入磁盘就已经返回, 所以当
broker发生故障时,有可能会丢失数据
.
[acks =1 ]
- 当ack=1时, producer等待broker的ack, 等待在partition的leader落盘成功后并应答完成, 但是还没有开始同步副本, 如果
在follower同步副本成功之前leader故障, 那么将会丢失数据
.
[acks =-1 ]
- 当 ack=-1时, producer等待broker 的ack, 而且是等待在partition的leader和follower全部落盘成功后返回的ack, 但是如果
在follower同步完成后, broker发送ack之前,leader发生故障, 将会从剩下的follower中重新选举新的leader, 然后producer会重新向新的leader发送数据, 由于此前数据已经同步过了, 此时就会造成数据的重复.
Follower 故障:
ISR.
代码写法:
Properties prop = new Properties();
// 设置 acks
prop.put(ProducerConfig.ACKS_CONFIG, "all"); //ack的取值, [1, -1, all]
// 重试次数 retries,默认是 int 最大值, 2147483647
prop.put(ProducerConfig.RETRIES_CONFIG, 3);
数据可靠性分析
2.3.4 生产经验–数据的去重
1. 至少一次(At least once)
至少一次(at least once) =
ack = -1
+分区副本数 >=2
+ISR中的副本数 >=2
;
- 将服务器的ack级别设置为-1, 即leader,follower均落盘后再发送ack,可以保证producer到server之间不会丢失数据,server至少能够收到一次数据, 即At least once(至少一次)语义,
- At least once, 可以
保证数据不丢失, 但是数据有可能会重复
2. 最多一次(At most once)
最多一次(At most once) =
ack = 0
.
- 将服务器ack设置为0, 即leader,follower均未落盘, 就发送了ack, 可以保证生产者每条消息只会被发送一次, 即At Most Once(最多一次)语义.
- At most once, 可以
保证数据不重复, 但可能会丢失数据
3. 精准一次(Exactly Once)
- At least once, 可以保证数据不丢失, 但是不能保证数据不重复, 然而对于一些比较重要信息, 比如说交易数据, 下游数据消费者要求数据既不重复也不丢失, 即 Exactly Once语义.
- 在0.11之前的kafka只能保证数据不丢失, 然后在下游消费者处对数据做全局去重. 对于多个下游应用的情况, 每个都需要单独做全局去重, 无疑对性能有巨大的影响.
- 0.11版本的Kafka引入了一项重大特性: 幂等性, 所谓的
幂等性就是指Producer无论向Server发送多少次重复数据, Server端都只会持久化一条, 保证了数据的不重复.
, - 幂等性结合At Least Once 语义, 就构成了Kafka的Exactly Once 语义, 即
- At Least Once(ack = -1, 分区副本 >= 2, ISR >=2) + 幂等性 = Exactly Once
幂等性的实现原理和特点(即重复数据的判断标准)?
- Kafka 的幂等性实现其实就是将原来下游需要做的去重放在了数据上游,
- 开启幂等性的Producer在初始化的时候会被分配一个
PID
(不是进程号噢, 而是Producer ID), 发往同一Partition
的消息会附带Sequence Number
(单调递增), 而Broker端会对<PID, Partition, SeqNumber>做缓存, 当具有相同主键的消息提交时, Broker只会持久化一条. - 但是PID重启就会变化(重新分配), 同时不同的Partition也具有不同的主键, 所以幂等性
无法保证跨分区跨会话
的Exactly Once, 即幂等性只能保证单分区单回话内不重复.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9llNZ5T-1650032265204)(2022-04-13-14-55-00.png)]
如何启用Kafka 的幂等性呢?
要启用幂等性, 只需要将Producer的参数中
enable.idompotence
设置为true即可.
2.3.x Kafka 事务原理 (待补充)
2.6 Kafka 事务
2.3.5 故障处理机制(数据存储一致性
的保证)
LEO
(log end offset):每个副本最大的Offset偏移量, 标识当前.log文件(数据文件, 别自己混乱了)下一条待写入消息的offset偏移量.
HW
(high watermark):消费者能够见到的最大的Offset(即消费者只能拉取到这个偏移量前面的消息), 它是IRO队列中最小的LEO.
参考资料: 1. LEO和HW, 及其更新流程
-
Follower故障
- follower 发生故障后会被临时踢出ISR, 待该follower恢复后, follower会读取本地磁盘记录的上次的HW(高水位), 并将log文件高于HW的部分截取掉, 从HW开始向leader进行同步, 等该follower的LEO大于等于该partition的HW, 即follower追上leader之后, 就可以重新加入ISR了.
-
Leader故障
- leader发生故障之后, 会从ISR中选出一个新的leader, 之后, 为保证多个副本之间的数据一致性, 其余的follower会先将自己的各自的log文件高于HW的部分截掉, 然后重新的leader中同步数据.
- leader发生故障之后, 会从ISR中选出一个新的leader, 之后, 为保证多个副本之间的数据一致性, 其余的follower会先将自己的各自的log文件高于HW的部分截掉, 然后重新的leader中同步数据.
2.3.7 生产经验–数据有序
Kafka如何保证全局有序?
https://jishuin.proginn.com/p/763bfbd5c833
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律