《深入理解Kafka核心设计与实践原理》读书笔记

目录

Kafka为什么不支持减少分区

Topic分区

日志存储

日志索引

日志清理

日志存储直接使用磁盘,而不是内存,怎么保证速度

深入服务器

为什么不支持读写分离

提高可靠性方法


Kafka为什么不支持减少分区

代码逻辑是可以实现,但是比较复杂,而且使用场景很少,完全可以新建一个topic去替代

第一,如果不保留原来分区的消息,可靠性得不到保障。

第二,把消息移到其他分区的话,如果直接存储到尾部则会破坏消息的时间戳排序,如果要做时间排序则会移动队列消息,消息很大的话,内部复制会占用很大资源。同时事务性,分区和副本问题都要考虑。

删除主题方式

第一,使用kafka.topics.sh脚本,会在zk生成一个标记节点,标记主题删除状态,然后kafka去删除。

第二,手动删除zk文件

Topic分区

两种方式操作topic:第一脚本,第二客户端api(可检验创建的topic是否符合要求)

分区follower不会提供服务,只是同步leader的数据

分区和leader的平衡,不代表集群的平衡。

分区平衡

1. 在生产环境中不建议将auto.leader.rebalance.enable设置为默认的true,因为会定时检查平衡率,自动做分区平衡,如果在业务高峰可能引起阻塞和超时。控制权在自己手里,通过监控然后手动平衡。

2. 正常情况下,优先副本作为leader节点,在机器宕机follower变成leader,重启后不会变成leader,造成分区不平衡。

3. 在实际生产环境中,一般使用 path-to-json-file 参数来分批、手动地执行优先副本的选举操作

下线机器或者扩容:执行分区重分配

分区限流控制节点复制数据的速率

设置分区数(分区数太多可能会超过系统文件句柄限制)

1. ulimit-n 65535 针对当前用户当前shell有效。

2. 在/etc/security/limits.conf文件中设置对所有用户所有shell有效,修改文件需要重启才能生效

3. 也可以通过在/etc/profile文件中添加ulimit的设置全局生效

分区数多少为好:不是越多越好,分区数少的时候增加会提升吞吐量,但到达一个阈值时不升反降。会受到磁盘,IO,系统调度的影响。太多分区可能超出文件句柄的限制,也会使Kafka的正常启动和关闭消耗更多时间,加重清除日志负担。

日志存储

每种文件同时切换Segment分片。一个topic的一个分区的一个副本有一个日志目录

日志索引

稀疏索引,每当写入一定量消息时(由broker端参数控制,默认4K),对应增加一个偏移量和时间戳索引项。

偏移量索引文件:偏移量到消息物理地址,单调递增,二分查找。找Segment分片文件是用跳跃表。

时间戳索引文件:时间戳到偏移量映射,单调递增,二分查找,需要回表查询。找Segment顺序查找,读每个时间戳文件的最大时间戳,没有就读文件更新时间

Segment分文件条件:日志文件和索引文件太大(默认日志文件是1G),分片文件最小时间戳与当前系统时间戳差值大于设置值,偏移量大于INT最大值

日志清理

有两个策略,可配置,delete和compact。有定时任务,默认每5分钟检查一次,要删除的话,在文件后面加.delete后缀,最后有一个延迟线程专门做日志删除

1.日志删除

1)基于时间。可以配置,默认是7天。

2) 基于日志大小。总的日志大小,.log文件,超过配置值则从第一个日志分段开始删除diff量。每个日志分段默认最大值是1G

3)基于日志起始偏移量。起始偏移量可配置,低于这个偏移量则删除

2.日志压缩

相同key,则保留最大版本的value,其余会删除。针对相同key的消息的合并清理。

日志存储直接使用磁盘,而不是内存,怎么保证速度

1,顺序读写,而不是随机读写,经过测试顺序读写速度比内存随机读写快

2,利用操作系统的磁盘页缓存功能实验高速读写。读写首先在缓存中操作,写时先写入缓存,读时从缓存查询,没有再读磁盘,读取磁盘后会放到页缓存。最后由操作系统刷盘,也可以手动异步或者强制刷盘,但是不建议手动。页缓存相比java虚拟机管理内存可以简化开发,也比虚拟机更安全,因为Kafka进程挂了重启还可以读取到页缓存的数据。而机器宕机导致数据未刷盘的问题,应该是由多副本来保障。但要警惕非活跃内存被交换到磁盘的情况,设置swap的积极程度不应该太大,也不应该是0。太小当内存不够用时,无法交换内存,系统不可用。设置为1可以保证系统内存不够用时跟磁盘做交换,也不会频繁做内存交换。

4种磁盘调度算法:

NOOP:FIFO队列,大致的先来后到顺序操作,相邻I/O地址会做请求合并

CFQ(默认):按照I/O请求的地址排序,最大限度地做顺序访问磁盘,但可能会带来“饥饿”

DEADLINE:在CFQ基础上,分别为读和写I/O提供了一个FIFO队列,读最大等待时间500ms,写最大等待时间5s,解决了“饥饿”问题。

ANTICIPATORY:上面几个是针对零散I/O,ANTICIPATORY为了满足随机I/O和顺序I/O的混合场景。为每个读设置6ms的等待时间窗口,如果6ms内收到了相邻位置的读I/O请求,则立即满足。通过读取/写入延迟换取最大的读写吞吐量。

3,零拷贝技术。零拷贝是说内核模式没有数据拷贝,使用DMA技术拷贝文件句柄到Buffer Socket里,然后直接把文件数据传递到网卡设备。如果走了拷贝数据到内核的方式,会产生四次数据拷贝,四次上下文切换。保存下图

深入服务器

定时任务:时间轮TimingWheel+延迟队列DelayQueue。时间轮复杂任务的增删,延迟队列负责时间推进,相辅相成。

控制器:监听主题、分区、broker的变化。更新集群元数据变化,读取和管理主题,分区,broker数据

优雅关闭:同步消息到磁盘,下一次上线就不用恢复日志。迁移leader副本,减少分区不可用时间。

可靠性探究(衡量可靠性:多少个9)

副本滞后Leader一定时间后会被移出ISR,默认是10秒,(或者滞后消息大小超过一定值,默认4000,由于很难界定大小,从0.9.x版本开始移除了),有参数可设置

引入LeaderEpoch也解决不了消息丢失,但可以避免消息不一致。

为什么不支持读写分离

因为有数据一致性和延迟问题消息写入然后主从同步要经过:网络-主节点内存-主节点磁盘-网络-从节点内存-从节点磁盘。由于多副本设计,主读写已经具备负载均衡功能了,这样代码实现更简单,不容易出错,就没必要做主读从写了。可以通过运维,监控告警监控负载不均衡的情况。

负载不均的场景:

1.broker端分区分配不均,2.生产者写入消息不均,3.消费者消费消息不均,4.leader副本的切换不均。

针对第1种情况,创建主题时候尽量使分区均衡。第2和第3主写从读也无法解决。第4种可以使用优先副本选举来达到leader副本的平衡。

提高可靠性方法

1,生产者发送消息有三种方式:发后即忘(消息发送失败也不会收到通知),同步,异步。提高发送消息可靠性可以设置ack=-1或者all,让所有ISR里的副本都拉取成功到消息才返回生产者成功通知。ack=1代表主要leader写入成功就返回,如果此时leader宕机就有可能丢失消息。acks=0,不会等待服务器的响应。

2,发送消息时,对于可重试异常设置重试策略。比如NetworkException,如果设置了重试会自动做重试。重试次数参数retries,默认是0。重试间隔时间retry.backoff.ms。retries大于0可能引起负面影响,由于max.in.flight.requests.per.connection(该参数指定了生产者在收到服务器响应之前可以发送多少个消息。它的值越高,就会占用越多的内存,不过也会提升吞吐量。把它设为 1 可以保证消息是按照发送的顺序写入服务器的,即使发生了重试。)参数默认是5,重试影响消息的顺序性。如果retries大于0,那么max.in.flight.requests.per.connection要设置为1,这样放弃了吞吐。如果一些需要快速失败的场景,设置retries>0会造成延迟反馈。

3,提供isr副本数,min.insync.replicas。要求isr的副本数最少多少个,小于的话发送消息会抛出NotEnoughReplicasException。这里是可用性和可靠性的选择了,如果设置大于0,那么在isr为0,非ISR不为0的情况下也不可用。

4,是否可以从非ISR集合中选择leader:unclean.leader.election.enable。默认是false,不能从非ISR集合选择leader,选择了可靠性。

5,同步刷盘策略。log.flush.interval.messaages和log.fluseh.interval.ms。建议使用系统刷盘策略,同步刷盘操作损耗性能,可靠性由多副本来保障。

6,消费端参数。enable.auto.commit,开启自动位移提交。默认是true,可能会造成重复消费和消息丢失。为了高可靠,应该设置为false,只有消费完成才能提交位移,这样避免消息的丢失,但也可能会重复消费,要做幂等。如果某个消息一直消费不成功,为了不影响整体的消费进度,可以把这个消息放到死信队列。另外还有兜底功能:回溯消费,可以找回漏掉的消息。

数据导入导出,迁移:分区重分配是在单集群里做数据的复制,Mirror Maker是在不同集群之间做数据的迁移,通过消费和再生产的原理。可以从关系型数据库,NoSQL数据库,日志,文件中向Kafka导入和导出消息。

默认事务隔离级别是读未提交

计算消息滞后Lag

无事务或者事务隔离级别为未提交,则Lag = HW - ConsumerOffset

如果事务级别是读提交,则Lag = LSO - ConsumerOffset

LSO:对于未完成的事务,是事务消息的第一条消息偏移量。对于已完成的事务,等于HW

延迟队列的实现:Kafka原生不支持延迟,可以设置不同延迟级别队列来定时拉取消息,类似Rocketmq,或者用时间轮。现实中很少用到

死信队列:即消费者不能处理,或者不想处理,也或者是处理异常的消息。Kafka不存在,可以自己根据概念封装。

幂等性:Kafka 自 0.11 版本开始引入了幂等性和事务,Kafka的幂等性是指单个生产者对于单分区单会话的幂等,而事务可以保证原子性地写入多个分区,即写入多个分区的消息要么全部成功,要么全部回滚,这两个功能加起来可以让Kafka具备EOS (Exactly Once Semantic)的能力

参考:《深入理解Kafka核心设计与实践原理》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值