在聊 Kafka 高牢靠之前,先在评论区来波 RNG NB 好不好!
什么叫可靠性?
大家都晓得,零碎架构有三高:「高性能、高并发和高可用」,三者的重要性显而易见。
对于任意零碎,想要同时满足三高都是一件十分艰难的事件,大型业务零碎或者传统中间件都会搭建简单的架构来保障。
除以上三种模式之外,还有一个指标方向也很重要,那就是 高牢靠,甚至你可能会将它和「高可用」混同起来。
事实上两者并不一样,高可用会更偏差于整体服务的可用性,避免零碎宕机等等。而高牢靠是指 数据 的可靠性保障嘛,你能够了解”高牢靠“相比于零碎三高会是一个 更细一点 的概念。
那么什么是数据的高牢靠呢,总结一下就是 零碎要提供牢靠的数据撑持,不能产生失落、反复等谬误景象。
所以每个开源中间件在公布版本时都会通过文档申明本人是超牢靠的,就像520 那天每一位暖男说的那样。
咱明天的配角 kafka 就是这么一个例子。
一些重要概念
因为有一段时间没讲音讯队列了嘛,为了帮忙你更好了解文章,咱们来先温习一下kafka 的根底概念:
- record:音讯,音讯队列根底通信单位
- topic:主题,目标就是将音讯进行分类,不同业务类型的音讯通常会被散发到不同的主题
- partition:分区,每个主题能够创立多个分区,每个分区都由一系列有序和不可变的音讯组成
- replica:正本,每个分区都有一个至多个正本存在,它的次要作用是存储保留数据,以 日志 (Log) 对象 的模式体现。正本又分为 leader 正本和 follower 正本
- offset:偏移量,每一个音讯在日志文件中的地位都对应一个按序递增的偏移量,你能够了解为相似数组的存储模式
- producer:生产者,生产音讯的那一方
- consumer:消费者,通常不同的业务都会有一到多个消费者组成消费者集群
- broker:代理,一个 Kafka 集群由一个或多个 Kafka 实例形成,每一个 Kafka 实例就称为代理
如上图所示,一共存在主题 1 和主题 2,主题 1 有两个分区,主题 2 只有一个分区,并且每个分区都存在一个 leader 正本和两个 follower 正本,它们散布在每个不同的代理节点上。
partition 里只有 leader 正本负责与生产者、消费者之间数据的交互,follower 正本会定期从 leader 正本拉取数据以保障整个集群数据可用性。
如何保证数据高牢靠
Kafka 是通过 正本机制 实现数据的存储的,所以就须要一些机制保证数据在跨集群的正本之间可能牢靠地传输。
1. 正本同步汇合
业务数据封装成音讯在零碎中流转,因为各个组件都是散布在不同的服务器上的,所以主题和生产者、消费者之间的数据同步可能存在肯定的 时间延迟,Kafka 通过提早范畴划分了几个不同的汇合:
AR(Assigned Replicas)
指的是 曾经调配数据的分区正本,通常指的是 leader 正本 + follower 正本。
ISR(In Sync Replicas)
指的是 和 leader 正本数据放弃同步的正本汇合。当 follower 正本数据和 leader 正本数据放弃同步,那么这些正本就处在 ISR 外面,ISR 汇合会依据数据的同步状态动态变化。
OSR(Out Sync Replicas)
一旦 follower 正本的数据同步进度跟不上 leader 了,那么它就会被放进叫做 OSR 的汇合里。也就是这个汇合蕴含的是不处于同步状态的分区正本。
OK,那有什么规范判断它是同步还是不同步呢?
通过 replica.lag.time.max.ms 这个参数来设置数据同步时间差,它的默认值是 10s。
一旦从分区正本和主分区正本的音讯相差 10s 以上,那么就认为音讯处于 OSR 不同步的状态。若 follower 处于 OSR 汇合里,那么在选取新的 leader 的时候就不会选举它作为新 leader。
2.ACK 应答机制
咱们刚刚说了 kafka 是通过 ack 来发送数据同步信号的,那 信号发送频率 又有几种设定呢?
- ack = 0
生产者发送一次音讯就不再发送。不论是否发送胜利,若收回去的音讯处于通信的路上就失落,或者还未做磁盘长久化操作,那么音讯就可能失落。
它的益处就是性能很高,你想呀你发送音讯都不须要期待对方回复就继续发送下一批,那么音讯期待的工夫就节俭进去了。同一时间范畴内能比他人解决更多数据,毛病就是它的可靠性真的很低,数据真的是说丢就丢。
- ack = 1
leader 接管到音讯并且写入到本地磁盘后就认为音讯解决胜利。这种形式可靠性会比上一种好一些,当 leader 接管到音讯并且写入到本地磁盘后就认为音讯解决胜利,不管 follower 是否同步完这条音讯就会返回给 producer。
然而如果此刻 partition leader 所在的 broker 宕机了,如果那么数据也可能会失落,所以 follower 正本的数据同步就很重要。
Kafka 默认就采纳这种形式。
- ack = -1
producer 只有收到分区内所有正本的响应 ACK 才会认为音讯曾经 push 胜利。
这种形式尽管对于数据的牢靠保障做得很好,然而就是性能很差,影响吞吐量,所以个别也不会采取。
那么它就相对牢靠吗?也不肯定。最重要的还是取决于正本数据是否同步实现。若 producer 收到响应音讯前 leader 正本挂掉,那么 producer 会因未收到音讯反复发送音讯,那就可能造成数据反复。怎么解决呢?只有保障业务幂等就行。
咱们能够通过 request.required.acks 这个参数管制音讯的发送频率。
如果感觉文章不错,能够微信搜一搜「敖丙 」第一工夫浏览,关注后回复【 材料】有我筹备的一线大厂面试材料和简历模板
3. 音讯语义
音讯集群整体是一个简单的零碎,所以过程中可能会因为各种起因导致 消息传递出错,Kafka 对于这些可能遇到的场景定义了对应的的音讯语义。
at most once
它代表音讯可能被消费者生产 0 次或者 1 次。若场景如下:
- 音讯从 partition 分发给消费者集群
- 消费者把本人收到的音讯通知集群,集群收到之后 offset 就会往后挪动
- 消费者将数据入库做长久化
你肯定想到了。在第三步消费者将音讯入库时若因任何起因消费者 A 挂了,那么在将消费者切换到集群的消费者 B 后,数据还没入库呢。此时 partition 是浑然不知的呀,那么这就会造成一个问题:数据失落。
at least once
它代表 partition 散发的音讯至多被生产一次。其通信过程如下:
- 音讯从 partition 分发给消费者集群
- 消费者将数据入库做长久化
- 消费者把本人收到的音讯通知集群,集群收到之后 offset 就会往后挪动
假如 consumer group 在数据入库之后,在将数据返回给 partition 的过程中消费者 A 挂了,那么 partition 会因为接管不到响应 ACK 而从新发送数据,此时消费者 B 可能再次将原先的音讯入库,这就造成了数据反复了。
在没有做任何幂等性爱护的状况下,像反复转账,重付叠加积分这种业务,那么后果可能是致命的。
exactly once
代表音讯正好能被生产一次,不失落,不反复。
在 at least once 的状况根底上,假如 consumerA 在返回 ack 给 partition 的过程中宕机了。那么 consumerB 不会跟着 partition 的 offset 走,它会 先去数据库外面查看最新消息对应的偏移位,再依据这个偏移位返回 Kafka 集群从对应的偏移地位登程,这就能够防止音讯反复和音讯失落。
不晓得有多少小伙伴看到这里的,如果感觉目前为止写的还不错的,能够帮忙点个赞让,让我看看有多少好学的宝宝。
4. 数据截断机制
咱们结尾说了真正解决数据的是 leader 正本,follower 正本只负责数据的同步和保留,那如果因为 leader 宕机了二者数据不统一会怎么样呢?
在讲一致性保障过程之前还需理解两个 Kafka 用于示意正本数据同步的概念:
HW(High Watermark):中文翻译为高水位,用来体现正本间数据同步的绝对地位,consumer 最多只能生产到 HW 所在的地位,通过 HW 咱们能够判断数据对正本是否可见。
LEO(Log End Offset):下一条待写入音讯的记录地位。
leader 正本从生产者获取音讯,follower 正本实时从 leder 同步数据,此时它们的同步数据是统一的都同步到 2 这个地位,并且下一个写入的音讯都是偏移位 4:
假如因为意外 leader 产生宕机,follower 即被选为新 leader,尔后从生产者写入最新的偏移位 4 和 5:
过了一段时间原 leader 通过修复复原服务,它就会发现自己和新 leader 的数据是不统一的:
为了保证数据一致性就必须强行让一方斗争。因为 数据是一直在刷新的 ,所以旧 leader 此时的优先级会小于新 leader,因而它会将本人的数据截断到 与新 leader 雷同的 HW 和 LEO 地位 ,确保和新 leader 的数据肯定雷同,这就是 Kafka 数据截断机制。
5. 数据清理机制
同其它中间件一样,Kafka 的次要作用是通信,所以即便是将数据保留在磁盘上它还是会占用肯定空间。为了节约存储空间它会通过一些机制对过期数据进行清理。
日志删除
日志删除会间接删除日志分段,kafka 会保护一个定时工作来周期性检查和删除「过期数据」。
- 基于工夫的日志删除
它在每一个日志段文件外面都保护一个 最大工夫戳 来确认以后配置的删除工夫,只有日志段写入新音讯该字段都会被更新。一个日志段被写满了之后就不会再接管新的音讯,它会去创立一个新的日志段文件往里面写数据。
每一个日志段文件被写满之后它的最大的工夫戳都是放弃不变的,Kafka 只有通过以后工夫与最大工夫戳进行比拟就能够判断该日志段文件是否过期。
Kafka 默认配置 log.retention.hours = 168,也就是 7 天的日志保留工夫。
- 基于容量大小的日志删除
这和以上是殊途同归的形式,只不过这次从工夫换成了空间。
Kafka 会通过每个日志段空间的大小计算一个总容量阈值,而后计算出以后的理论空间大小和总容量阈值的差值,如果这个差值大于单个日志段文件的大小那么就会删除掉最旧的那个日志段文件,反之则不做任何解决。
同理,这个阈值也能够通过 log.retention.bytes 参数来设置。
日志压缩
Kafka 的音讯是由键值组成的,如果日志段里存在多条雷同 key 然而不同 value 的数据,那么它会选择性地革除旧数据,保留最近一条记录。
具体的压缩形式就是创立一个 检查点文件,从日志起始地位开始遍历到最大完结地位,而后把每个音讯的 key 和 key 对应的 offset 保留在一个固定容量的 SkimpyOffsetMap 中。
这样后面的值就会被前面的笼罩掉,如果日志文件里存在雷同的 key 只有最新的那个会被保留。
总结
Kafka 通过 ACK 应答机制保障了不同组件之间的通信效率,通过正本同步机制、数据截断和数据清理机制实现了对于数据的管理策略,保障整个零碎运行效率。
作为一款高性能又同时兼顾高可靠性的消息中间件来说,Kafka 能吹的点切实太多。如果本篇文章对你有所帮忙,点击一下右下角的大拇指,下一次咱们来具体解说Kafka 是如何实现正本间数据传递的。
你晓得的越多,不晓得的越多,各位的点赞评论都对我很重要,如果这篇文章有帮忙你多一点点理解 Kafka 的话,能够在评论区来一波“变得更强”。
也心愿你的 bug 和上面这张图一样,🤺 退 🤺退 🤺退!咱们下次见。
文章继续更新,能够微信搜一搜「敖丙 」第一工夫浏览,关注后回复【 材料】有我筹备的一线大厂面试材料和简历模板,本文 GitHub https://github.com/JavaFamily 曾经收录,有大厂面试残缺考点,欢送 Star。