《我想进大厂》之kafka夺命连环11问(二)

wg204wg
发布于 2022-6-10 16:00
浏览
0收藏

 

那你跟我再具体讲讲分区分配策略?
主要有3种分配策略:

Range

不知道咋翻译,这个是默认的策略。大概意思就是对分区进行排序,排序越靠前的分区能够分配到更多的分区。

比如有3个分区,消费者A排序更靠前,所以能够分配到P0\P1两个分区,消费者B就只能分配到一个P2。

如果是4个分区的话,那么他们会刚好都是分配到2个。

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
但是这个分配策略会有点小问题,他是根据主题进行分配,所以如果消费者组订阅了多个主题,那就有可能导致分区分配不均衡。

比如下图中两个主题的P0\P1都被分配给了A,这样A有4个分区,而B只有2个,如果这样的主题数量越多,那么不均衡就越严重。

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
RoundRobin

也就是我们常说的轮询了,这个就比较简单了,不画图你也能很容易理解。

这个会根据所有的主题进行轮询分配,不会出现Range那种主题越多可能导致分区分配不均衡的问题。

P0->A,P1->B,P1->A。。。以此类推

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
Sticky

这个从字面看来意思就是粘性策略,大概是这个意思。主要考虑的是在分配均衡的前提下,让分区的分配更小的改动。

比如之前P0\P1分配给消费者A,那么下一次尽量还是分配给A。

这样的好处就是连接可以复用,要消费消息总是要和broker去连接的,如果能够保持上一次分配的分区的话,那么就不用频繁的销毁创建连接了。

来吧!如何保证消息可靠性?
消息可靠性的保证基本上我们都要从3个方面来阐述(这样才比较全面,无懈可击)

生产者发送消息丢失

kafka支持3种方式发送消息,这也是常规的3种方式,发送后不管结果、同步发送、异步发送,基本上所有的消息队列都是这样玩的。

  1. 发送并忘记,直接调用发送send方法,不管结果,虽然可以开启自动重试,但是肯定会有消息丢失的可能
  2. 同步发送,同步发送返回Future对象,我们可以知道发送结果,然后进行处理
  3. 异步发送,发送消息,同时指定一个回调函数,根据结果进行相应的处理
    为了保险起见,一般我们都会使用异步发送带有回调的方式进行发送消息,再设置参数为发送消息失败不停地重试。

acks=all,这个参数有可以配置0|1|all。

0表示生产者写入消息不管服务器的响应,可能消息还在网络缓冲区,服务器根本没有收到消息,当然会丢失消息。

1表示至少有一个副本收到消息才认为成功,一个副本那肯定就是集群的Leader副本了,但是如果刚好Leader副本所在的节点挂了,Follower没有同步这条消息,消息仍然丢失了。

配置all的话表示所有ISR都写入成功才算成功,那除非所有ISR里的副本全挂了,消息才会丢失。

retries=N,设置一个非常大的值,可以让生产者发送消息失败后不停重试

kafka自身消息丢失

kafka因为消息写入是通过PageCache异步写入磁盘的,因此仍然存在丢失消息的可能。

因此针对kafka自身丢失的可能设置参数:

replication.factor=N,设置一个比较大的值,保证至少有2个或者以上的副本。

min.insync.replicas=N,代表消息如何才能被认为是写入成功,设置大于1的数,保证至少写入1个或者以上的副本才算写入消息成功。

unclean.leader.election.enable=false,这个设置意味着没有完全同步的分区副本不能成为Leader副本,如果是true的话,那些没有完全同步Leader的副本成为Leader之后,就会有消息丢失的风险。

消费者消息丢失

消费者丢失的可能就比较简单,关闭自动提交位移即可,改为业务处理成功手动提交。

因为重平衡发生的时候,消费者会去读取上一次提交的偏移量,自动提交默认是每5秒一次,这会导致重复消费或者丢失消息。

enable.auto.commit=false,设置为手动提交。

还有一个参数我们可能也需要考虑进去的:

auto.offset.reset=earliest,这个参数代表没有偏移量可以提交或者broker上不存在偏移量的时候,消费者如何处理。earliest代表从分区的开始位置读取,可能会重复读取消息,但是不会丢失,消费方一般我们肯定要自己保证幂等,另外一种latest表示从分区末尾读取,那就会有概率丢失消息。

综合这几个参数设置,我们就能保证消息不会丢失,保证了可靠性。

OK,聊聊副本和它的同步原理吧?
Kafka副本的之前提到过,分为Leader副本和Follower副本,也就是主副本和从副本,和其他的比如Mysql不一样的是,Kafka中只有Leader副本会对外提供服务,Follower副本只是单纯地和Leader保持数据同步,作为数据冗余容灾的作用。

在Kafka中我们把所有副本的集合统称为AR(Assigned Replicas),和Leader副本保持同步的副本集合称为ISR(InSyncReplicas)。

ISR是一个动态的集合,维持这个集合会通过replica.lag.time.max.ms参数来控制,这个代表落后Leader副本的最长时间,默认值10秒,所以只要Follower副本没有落后Leader副本超过10秒以上,就可以认为是和Leader同步的(简单可以认为就是同步时间差)。

另外还有两个关键的概念用于副本之间的同步:

HW(High Watermark):高水位,也叫做复制点,表示副本间同步的位置。如下图所示,0~4绿色表示已经提交的消息,这些消息已经在副本之间进行同步,消费者可以看见这些消息并且进行消费,4~6黄色的则是表示未提交的消息,可能还没有在副本间同步,这些消息对于消费者是不可见的。

LEO(Log End Offset):下一条待写入消息的位移

《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区

 hw

副本间同步的过程依赖的就是HW和LEO的更新,以他们的值变化来演示副本同步消息的过程,绿色表示Leader副本,黄色表示Follower副本。

首先,生产者不停地向Leader写入数据,这时候Leader的LEO可能已经达到了10,但是HW依然是0,两个Follower向Leader请求同步数据,他们的值都是0。

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
然后,消息还在继续写入,Leader的LEO值又发生了变化,两个Follower也各自拉取到了自己的消息,于是更新自己的LEO值,但是这时候Leader的HW依然没有改变。

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
此时,Follower再次向Leader拉取数据,这时候Leader会更新自己的HW值,取Follower中的最小的LEO值来更新。

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
之后,Leader响应自己的HW给Follower,Follower更新自己的HW值,因为又拉取到了消息,所以再次更新LEO,流程以此类推。

 《我想进大厂》之kafka夺命连环11问(二)-鸿蒙开发者社区
你知道新版本Kafka为什么抛弃了Zookeeper吗?
我认为可以从两个个方面来回答这个问题:

首先,从运维的复杂度来看,Kafka本身是一个分布式系统,他的运维就已经很复杂了,那除此之外,还需要重度依赖另外一个ZK,这对成本和复杂度来说都是一个很大的工作量。

其次,应该是考虑到性能方面的问题,比如之前的提交位移的操作都是保存在ZK里面的,但是ZK实际上不适合这种高频的读写更新操作,这样的话会严重影响ZK集群的性能,这一方面后来新版本中Kafka也把提交和保存位移用消息的方式来处理了。

另外Kafka严重依赖ZK来实现元数据的管理和集群的协调工作,如果集群规模庞大,主题和分区数量很多,会导致ZK集群的元数据过多,集群压力过大,直接影响到很多Watch的延时或者丢失。

OK,最后一个大家都问的问题,Kafka为什么快?
嘿,这个我费,我背过好多次了!主要是3个方面:

顺序IO

kafka写消息到分区采用追加的方式,也就是顺序写入磁盘,不是随机写入,这个速度比普通的随机IO快非常多,几乎可以和网络IO的速度相媲美。

Page Cache和零拷贝

kafka在写入消息数据的时候通过mmap内存映射的方式,不是真正立刻写入磁盘,而是利用操作系统的文件缓存PageCache异步写入,提高了写入消息的性能,另外在消费消息的时候又通过sendfile实现了零拷贝。

关于mmap和sendfile零拷贝我都专门写过,可以看这里:阿里二面:什么是mmap?

批量处理和压缩

Kafka在发送消息的时候不是一条条的发送的,而是会把多条消息合并成一个批次进行处理发送,消费消息也是一个道理,一次拉取一批次的消息进行消费。

并且Producer、Broker、Consumer都使用了优化后的压缩算法,发送和消息消息使用压缩节省了网络传输的开销,Broker存储使用压缩则降低了磁盘存储的空间。

我是艾小仙,过两天准备喷人。

 

文章转自公众号:艾小仙

分类
标签
已于2022-6-10 16:00:48修改
收藏
回复
举报
回复
    相关推荐