方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 topic 又可以有多个 Partition 组成,因此整个集群就可以适应任意大小的数据了;
可以提高并发,因为可以以 Partition 为单位读写了。
我们需要将 producer 发送的数据封装成一个 ProducerRecord 对象。
ProducerRecord(String topic, Integer partition, K key, V value)
ProducerRecord(String topic, Integer partition, Long timestamp, K key, V value)
ProducerRecord(String topic, K key, V value)
ProducerRecord(String topic, V value)
分区策略:
- (1)指明 partition 的情况下,直接将指明的值直接作为 partiton 值;
- (2)没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值;
- (3)既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后 面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition 值,也就是常说的 round-robin 算法。
ACK机制(保证生产者数据不丢失)
生产者将消息发送到broker,broker需要告诉生产者,你这个消息是否发送成功。如果未成功,生产者还可以采取重发的机制。
因为消息是发送到Partition,而Partition同时有多个副本。副本是需要实时同步消息的。
borker判断消息是否成功,是消息可靠落盘。
-
半数副本以上消息完成同步,就发送ack。延迟低,但选举新的 leader 时,容忍 n 台 节点的故障,需要 2n+1 个副本
-
全部副本完成消息同步,才发送ack。虽然延迟高,但 选举新的 leader 时,容忍 n 台节点的故障,需要 n+1个副本
Kafka 选择的第二种方案,虽然延迟高,但网络延迟对 Kafka 的影响较小,且为Kafka 的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
优化方案:ISR
leader 收到数据,所有 follower 都开始同步数据, 但有一个 follower,因为某种故障,迟迟不能与 leader 进行同步,那 leader 就要一直等下去, 直到它完成同步,才能发送 ack。这个问题怎么解决呢?
Leader 维护了一个动态集合in-sync replica set(和 leader 保持同步的 follower 集合)。通过参数replica.lag.time.max.ms设置时间阈值,当follower在规定时间未完成同步,则将该follower剔出这个集合。
当某些消息数据,不需要可靠。即生成者不需要ack机制时,对于不需要数据的可靠性时,kafka就没必要等 ISR 中的 follower 全部接收成功,才返回ack.所以 Kafka 为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,
acks 参数配置:
0:(只管发) producer 不等待 broker 的数据落盘就直接返回 ack,当broker故障时有可能丢失数据
1:(只保证broker数据落盘)producer 等待 broker 的 ack,partition的leader落盘成功后就不等follower同步便直接返回 ack
如果在 follower同步成功之前leader故障,那么将会丢失数据;
-1(all):(保证broker和所有follower数据落盘)producer 等待 broker 的 ack,partition 的 leader 和 follower 全部落盘成功后才 返回 ack。
但是如果在 follower 同步完成后,broker 发送 ack 之前,leader 发生故障,那么会 造成数据重复。
消息幂等性
kafka0.11版本之前解决消息幂等性,需要在每个消费端手动进行去消息判重(可以使用redis,或者mysql主键特性,将消费后的消息进行存储)这也是RocketMQ的消息幂等性的解决方案
kafka0.11版本后,borker支持了幂等性。将原来消费端手动需要做的去重放在了数据borker原生支持中。
要启用幂等性,只需要将 Producer 的参数中 enable.idompotence 设置为 true 即可。
原理: 开启幂等性的 Producer 在初始化的时候会被分配一个 PID,发往同一 Partition 的消息会附带 Sequence Number。而Broker 端会对<PID, Partition, SeqNumber>做缓存,当具有相同主键的消息提交时,Broker 只会持久化一条。但是 PID 重启就会变化,同时不同的 Partition 也具有不同主键,所以broker解决消息的幂等性无法保证跨分区跨会话的 Exactly Once。也就是说不能重启生产者。否则还是会产生重复消息