1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-08-01 16:28:03 +08:00

update kafka

This commit is contained in:
戴林峰 2023-09-30 12:07:43 +08:00
parent d9e554a4a6
commit 5d2169f076

View File

@ -114,10 +114,100 @@ Kafka 允许进行批量发送消息,先将消息缓存在内存中,然后
## Kafka中的消息是否会丢失和重复消费
Kafka在**生产端**发送消息和**消费端**消费消息时都可能会**丢失**一些消息。
### Producer消息丢失
生产者在发送消息时会有一个ack机制当acks=0或者acks=1时都可能会丢失消息。
> 背景知识Producer发送消息时是直接与Broker中的Leader Partition进行交互的然后其他的副本再从Leader Partition中进行数据的同步。因此在发送消息的时候Producer只需要找到对应Topic的Leader Partition进行消息发送即可。
>
> 消息发送的流程:
>
> 1. 将消息发送到对应Topic下的Leader Partition
> 2. Leader Partition收到消息并将消息写入Page Cache定时刷盘进行持久化顺序写入磁盘
> 3. Foller Partition 拉取Leader Partition的消息并同Leader Partition的数据保持一致待消息拉取完毕后再给Leader Partition回复ack确认消息。
> 4. 待Leader与Foller 同步完数据并收到所有ISR中的Replica副本的ack后Leader Partition会给Producer回复ack确认消息。
Producer端为了提升发送效率减少I/O操作发送数据的时候是将多个请求合并成一个个RecordBatch并将其转换成为Request请求**异步**将数据发送出去或者按时间间隔方式每隔一定的时间自动发送出去因此Producer端消息丢失更多是因为消息根本没有发送到Kafka Broker端。
因此,**导致Producer端消息没有成功发送有以下原因**
1. 网络原因由于网络原因数据根本没有到达Broker端。
2. 数据原因消息太大超出Broker承受的范围导致Broker拒收消息。
**Producer消息确认机制**
Producer端配置了消息确认机制来确认消息是否生产成功使用ack确认机制。
1. asks=0:只要发送就自认为成功并不进行消息接收成功的ack确认。
1. 不能保证消息是否发送成功。
2. 生产环境完全不可用。
2. acks=1:当Leader Partition接收成功时进行ack确认确认后表示成功
1. 只要Leader Partition存活就可以保证不丢失保证了吞吐量。
2. 生产环境中如果需要保证吞吐量可以用这个。
3. acks=-1或者all所有Leader Partition和Foller PartitionISR都接收成功时进行ack确认确认后表示成功。
1. 保证消息不丢失,但是吞吐量低。
2. 生产环境要求数据不能丢失可以采用该方式。
### Broker端丢失场景
Broker接收到数据后会将数据进行持久化存储到磁盘为了提高吞吐量和性能采用的是**异步批量刷盘的策略**也就是说按照一定的消息量和时间间隔进行刷盘这一点和mysql、redis很像。首先数据会背存储到**PageCache**中,至于什么时候将 Cache 中的数据刷盘是由「**操作系统**」根据自己的策略决定或者调用 fsync 命令进行强制刷盘,如果此时 Broker 宕机 Crash 掉,且选举了一个落后 Leader Partition 很多的 Follower Partition 成为新的 Leader Partition那么落后的消息数据就会丢失。既然Broker是异步刷盘的那么数据就有可能会丢失比如刷盘之前操作系统崩了并且Kafka中没有提供**同步刷盘**机制。)
虽然Kafka 通过「**多 Partition (分区)多 Replica副本机制」**已经可以最大限度的保证数据不丢失,但是当数据已经写入 PageCache 中但是还没来得及刷写到磁盘,此时如果所在 Broker 突然宕机挂掉或者停电,极端情况还是会造成数据丢失。
### Consumer端丢失场景剖析
> Consumer通过Pull模式主动的去Kafka集群中拉消息
>
> 1. 在消息拉取的过程中,有个消费者组的概念,多个 Consumer 可以组成一个消费者组即 Consumer Group每个消费者组都有一个Group-Id。同一个 Consumer Group 中的 Consumer 可以消费同一个 Topic 下不同分区的数据,但是不会出现多个 Consumer 去消费同一个分区的数据。
> 2. 拉取到消息后进行业务逻辑处理,待处理完成后,会进行 ACK 确认,即提交 Offset 消费位移进度记录。
> 3. 最后 Offset 会被保存到 Kafka Broker 集群中的 **__consumer_offsets** 这个 Topic 中,且每个 Consumer 保存自己的 Offset 进度。
Consumer端丢失消息主要体现在**消费端offset的自动提交**如果开启了自动提交万一消费到数据还没处理完此时consumer直接宕机未处理完的数据丢失了下次也消费不到了因为offset已经提交完毕下次会从offset处开始消费新消息。这种丢失情况的解决方法是**采用消费端的手动提交**
### 消息重复消费
**生产端消息重复发送**
生产端发送一条消息但是未得到broker的ack生产端又重新发了一条消息。这个时候两条消息都被broker接收到了消费端从broker拉取消息时就会造成重复消费。
> kafka新版本已经在broker中保证了接收消息的幂等性比如2.4版本),只需在生产者加上参数 props.put(“enable.idempotence”, true) 即可默认是false不开启。
>
> 新版本解决方案是producer发送消息时加上PID和Sequence NumberPID是Producer的唯一IDSequence Number是数据的序列号。
>
> broker接收到消息的时候就会检查有没有收到过这个消息根据PID和Sequence Number
**消费端消息重复消费**
消费端拉取一部分数据消费完成之后提交offset之前挂掉了此时offset未提交当前消息就会被重复消费。
解决办法添加分布式锁在offset提交之后再删key这样就保证了同一个消息只会被消费一次。
## Kafka顺序消息
Kakfa如果需要保证消息的顺序性则需要牺牲一定的性能。具体的顺序方式就是使用单一的消费者由一个消费者消费可以保证消息消费的顺序性但是消息发送的顺序性还是无法保证因为消息发送端有重传机制如果一次性发送两条消息前一条消息发送失败引发重传就会导致消息发送乱序。此时如果需要保证发送和接收的顺序那就使用发送的ack机制确认发送成功之后再发送下一条消息并且只能有一个Partition。但是这种方式会导致kafka性能低下。
**高效的解决方式**
类似于tcp发送的方式给每一个消息添加一个序号然后消费端每次拉取全部消息拉取回来之后再排序根据排序之后的数据进行处理。
## Kafka与其它MQ之间的区别为什么选择使用Kafka
**kafka相对于rocketMQ、rabbitMQ来说与它们最大的区别就是分布式存储这也是kafka高性能的最主要原因**。使用分布式存储理念,一个主题下多个分区,同时可以被多个消费者和生产者去使用,也增加了接受消息和消费消息的能力!
## 参考
+ Kafka官方文档https://kafka.apache.org/documentation
+ Kafka 设计架构原理详细解析https://blog.csdn.net/qq_32828253/article/details/110732652
+ Kafka为什么这么快https://zhuanlan.zhihu.com/p/147054382
+ Kafka为什么这么快https://zhuanlan.zhihu.com/p/147054382
+ Kafka如何保证消息不丢失https://zhuanlan.zhihu.com/p/459610418
+ kafka专题kafka的消息丢失、重复消费、消息积压等线上问题汇总及优化https://blog.csdn.net/qq_45076180/article/details/111561984