WJP
919497158@qq.com
Kafka学习笔记

什么是Kafka

Kafka最初是由LinkedIn公司采用Scala语言开发的一个多分区、多副本并且基于ZooKeeper协调的分布式消息系统,现在已经捐献给了Apache基金会。目前Kafka已经定位为一个分布式流式处理平台,它以 高吞吐、可持久化、可水平扩展、支持流处理等多种特性而被广泛应用。也有说Kafka定位为日志系统的

   Kafka 是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。

http://www.wjp.ink/wp-content/uploads/2020/12/image.png
Kafka和Zookeeper的关系

Kafka 使用Zookeeper 保存集群的元数据信息和消费者信息。Kafka 发行版自带了Zookeper ,可以直接从脚本启动,不过安装一个完整版的Zookeeper 也并不费劲。

什么是消息队列

在正式讨论Apache Kafka(以下简称Kafka)之前,先来了解发布与订阅消息系统的概念,并认识这个系统的重要性。

数据(消息)的发送者(发布者)不会直接把消息发送给接收者,这是发布与订阅消息系统的一个特点。发布者以某种方式对消息进行分类,接收者(订阅者)订阅它们,以便接收特定类型的消息。发布与订阅系统一般会有一个broker,也就是发布消息的中心点。

http://www.wjp.ink/wp-content/uploads/2020/12/image-2.png
发布订阅的关系

消息队列的意义

解耦
   允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。可恢复性系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

缓冲
   有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。

灵活性 & 峰值处理能力
   在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。削峰的典型场景,双11秒杀,所有请求打到队列上,用户那里全部给加载中状态,按队列顺序抢购。

异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

Kafka和消息队列的特有名词的概念

  

1.Producer :消息生产者,就是向 kafka broker 发消息的客户端;
2.Consumer :消息消费者,向 kafka broker 取消息的客户端;
3.Consumer Group (CG):消费者组,由多个 consumer 组成。消费者组内每个消费者负责消费不同  分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所有的消费者  都属于某个消费者组,即消费者组是逻辑上的一个订阅者。消费者以消费组为单位订阅kafka指定主题(Topic)或者主题的特定分区(Partition)。
4.Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker可以容纳多  个 topic。
5.Topic :可以理解为一个队列,生产者和消费者面向的都是一个 topic;
6.Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个   topic 可以分为多个 partition,每个 partition 是一个有序的队列;
7.Replica:副本,为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka   仍然能够继续工作,kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个   leader 和若干个 follower (leader 也是副本)。
8.leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 leader。
9.follower:每个分区多个副本中的“从”,实时从 leader 中同步数据,保持和 leader 数据的同步。  leader 发生故障时,某个 follower 会成为新的 follower。

http://www.wjp.ink/wp-content/uploads/2020/12/image-3.png
topic和customer

由上图可以看出,消费者的数量不应该比分区数多,因为多出来的消费者是空闲的,没有任何帮助。  消费者=分区数是比较好的。消费者从kafka中消费消息,但是生产速度大于消费速度产生消息堆积。可以增加消费者水平扩展

http://www.wjp.ink/wp-content/uploads/2020/12/image-4.png
partition和replica

为什么选择Kafka,Kafka的特点

• 高吞吐量:Kafka 每秒可以生产约 25 万消息(50 MB),每秒处理 55 万消息(110 MB)
• 持久性:可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如 ETL,以及实时应用程序。通过将数据持久化到硬盘以及replication 防止数据丢失。
• 分布式、易扩展:所有的 producer、broker 和 consumer 都会有多个,均为分布式的。无需停机即可扩展机器。
• 客户端状态维护:消息被处理的状态是在 consumer 端维护,而不是由 server 端维护。当失败时能自动平衡。

Kafka的主要应用场景:
日志收集、消息系统、用户活动跟踪运营指标、流式处理

消费者提交偏移量(已读的位置)offset
磁盘写入快:Kafka 写入操作采用追加写入( append )的方式,避免了磁盘随机写操作。
适合大数据环境
分布式存储数据,提供了更好的性能 可靠性 可扩展能力
利用磁盘存储数据,且按照主题、分区来分布式存放数据,持久化存储,提供海量数据存储能力
采用磁盘存储数据,连续进行读写保证性能,性能和磁盘的性能相关和数据量的大小无关

消费者与消费者组

一个消费组由一个或多个消费者实例组成,便于扩容与容错。kafka是发布与订阅模式,这个订阅者是消费组,而不是消费者实例。每一条消息只会被同一个消费组里的一个消费者实例消费,不同的消费组可以同时消费同一条消息,如下图

http://www.wjp.ink/wp-content/uploads/2020/12/image-5.png
topic和消费者组

为了实现还允许不同的传统的消息队列中消息只被消费一次的语义,kafka保证同一个消费组里只有一个消费者会消费一条消息(所以分区和消费者1:1是最好),kafka消费组同时消费一条消息,这一特性可以为消息的多元化处理提供了支持,kafka的设计理念之一就是同时提供离线处理和实时处理,因此,可以使用Storm这种实时流处理系统对消息进行实时在线处理,同时使用Hadoop这种批处理系统进行离线处理,还可以同时将数据实时备份到另一个数据中心,只需要保证这三个操作的消费者实例在不同consumer group 即可。

如果消费者超过一定时间没有发送心跳,那么它的会话(session)就会过期,组协调者会认为该消费者已经宕机,然后触发重平衡。

当我们调用poll()时,该方法会返回我们没有消费的消息。当消息从broker返回消费者时,broker并不跟踪这些消息是否被消费者接收到;Kafka让消费者自身来管理消费的位移,并向消费者提供更新位移的接口,这种更新位移方式称为提交(commit)。

在正常情况下,消费者会发送分区的提交信息到Kafka,Kafka进行记录。当消费者宕机或者新消费者加入时,Kafka会进行重平衡,这会导致消费者负责之前并不属于它的分区。重平衡完成后,消费者会重新获取分区的位移,下面来看下两种有意思的情况。

假如一个消费者在重平衡前后都负责某个分区,如果提交位移比之前实际处理的消息位移要小,那么会导致消息重复消费,如下所示:

http://www.wjp.ink/wp-content/uploads/2020/12/image-6.png
比如 通过轮询处理到10 但是提交的offset是2 ,那么下次的轮询会从2开始,中间的3-10会重复消费

假如在重平衡前某个消费者拉取分区消息,在进行消息处理前提交了位移,但还没完成处理宕机了,然后Kafka进行重平衡,新的消费者负责此分区并读取提交位移,此时会“丢失”消息,如下所示:

http://www.wjp.ink/wp-content/uploads/2020/12/image-7.png
比如 通过轮询处理到5, 但是提交的offset是11,那么下次的轮询会从12开始,中间的6-11会丢失

分区

默认的分区策略是:
•如果在发消息的时候指定了分区,则消息投递到指定的分区
•如果没有指定分区,但是消息的key不为空,则基于key的哈希值来选择一个分区
•如果既没有指定分区,且消息的key也是空,则用轮询的方式选择一个分区

分区重平衡

重平衡其实就是一个协议,它规定了如何让消费者组下的所有消费者来分配topic中的每一个分区。比如一个topic有100个分区,一个消费者组内有20个消费者,在协调者的控制下让组内每一个消费者分配到5个分区,这个分配的过程就是重平衡。重平衡是Kafka一个很重要的性质,这个性质保证了高可用和水平扩展。

重平衡的触发条件主要有三个:
•消费者组内成员发生变更,这个变更包括了增加和减少消费者。注意这里的减少有很大的可能是被动的,就是某个消费者崩溃退出了
•主题的分区数发生变更,kafka目前只支持增加分区,当增加的时候就会触发重平衡
•订阅的主题发生变化,当消费者组使用正则表达式订阅主题,而恰好又新建了对应的主题,就会触发重平衡

需要注意的是,重平衡过程中,消费者无法从kafka消费消息,这对kafka的TPS影响极大,而如果kafka集内节点较多,比如数百个,那重平衡可能会耗时极多。数分钟到数小时都有可能,而这段时间kafka基本处于不可用状态。所以在实际环境中,应该尽量避免重平衡发生。

了解了什么是重平衡,重平衡的缺点和触发条件后,我们先来看看重平衡的三种不同策略,然后说说应该如何避免重平衡发生。

kafka提供了三种重平衡分配策略
Range:这种分配是基于每个主题的分区分配,如果主题的分区分区不能平均分配给组内每个消费者,那么对该主题,某些消费者会被分配到额外的分区。
举例:目前有两个消费者C0和C1,两个主题t0和t1,每个主题三个分区,分别是t0p0,t0p1,t0p2,和t1p0, t1p1,t1p2。
那么分配情况会是:
•C0:t0p0, t0p1, t1p0, t1p1
•C1:t0p2, t1p2

http://www.wjp.ink/wp-content/uploads/2020/12/image-8.png
Range

    一个主题有三个分区,三个分区无法匹配两个消费者,势必有一个消费者分到两个分区

RoundRobin(轮询):
是基于全部主题的分区来进行分配的,同时这种分配也是kafka默认的rebalance分区策略。
     举例:目前有两个消费者C0和C1,两个主题t0和t1,每个主题三个分区,分别是t0p0,t0p1,t0p2,和t1p0,       t1p1,t1p2。
由于是基于全部主题的分区,那么分配情况会是:
•C0:t0p0, t0p2, t1p1
•C1:t0p1, t1p0, t1p2

http://www.wjp.ink/wp-content/uploads/2020/12/image-9.png
RoundRobin

因为是基于全部主题的分区来平均分配给消费者,所以这种分配策略能更加均衡得分配分区给每一个消费者

假设,组中每个消费者订阅的主题不一样,分配过程仍然以轮询的方式考虑每个消费者实例,但是如果没有订阅主题,则跳过实例。当然,这样的话分配肯定不均衡。
  举例:例如,假设有3个主题t0,t1,t2;其中,t0有1个分区p0,t1有2个分区p0和p1,t2有3个分区p0,p1和p2;有3个消费者C0,C1和C2;其中C0订阅t0,C1订阅t0和t1,C2订阅t0,t1和t2。最终订阅结果:
•C0:t0p0
•C1:t1p0
•C2:t1p1,t2p0,t2p1,t2p2

http://www.wjp.ink/wp-content/uploads/2020/12/image-10.png
RoundRobin每个消费者订阅不同主题

可以看出这样不均匀。
避免重平衡,避免消费者故障。增加分区,增加订阅的主题,增加消费者。还有kafka错误认为消费者挂掉的:增加心跳超时时间;增加心跳的频率(消耗更多资源);增加poll的间隔
在分布式系统中,由于网络问题你不清楚没接收到心跳,是因为对方真正挂了还是只是因为负载过重没来得及发生心跳或是网络堵塞。所以一般会约定一个时间,超时即判定对方挂了。

Sticky:主要是为了一定程度解决的重平衡非要重新分配全部分区的问题。称为粘性分配策略。
听名字就知道,主要是为了让目前的分配尽可能保持不变,只挪动尽可能少的分区来实现重平衡。

持久化

http://www.wjp.ink/wp-content/uploads/2020/12/image-11-1024x542.png

顺序追加的形式存储log文件
优势:读操作不会阻塞写操作和其他操作(因为读和写都是追加的形式,都是顺序的,不会乱,所以不会发生阻塞),数据大小不对性能产生影响; 线性访问磁盘,效率高  无容量限制

副本同步

Kafka中Topic的每个Partition有一个预写式的日志文件,虽然Partition可以继续细分为若干个Segment File,但是对于上层应用来说可以将Partition看成最小的存储单元(一个含有多个Segment文件拼接的“巨型”文件),每个Partition都由不可变的消息组成,这些消息被连续的追加到Partition中。

为了提高消息的可靠性,Kafka中每个Topic的partition有N个副本(replicas),其中N(大于等于1)是Topic的复制因子(replica fator)个数。Kafka通过多副本机制实现故障自动转移。当Kafka集群中一个Broker失效情况下仍然保证服务可用。在Kafka中发生复制时确保Partition的日志能有序地写到其他节点上。当N个replicas中有一个为Leader,其他都为Follower,Leader处理Partition的所有读写请求,与此同时,Follower会被动定期地去复制Leader上的数据。

http://www.wjp.ink/wp-content/uploads/2020/12/image-12.png
副本同步

在Kafka中并不是所有的副本都能被拿来替代主副本,所以在kafka的leader节点中维护着一个ISR(In syncReplicas)集合,翻译过来也叫正在同步中集合,在这个集合中的需要满足两个条件:
  节点必须和ZK保持连接
  在同步的过程中这个副本不能落后主副本太多

如果Leader发生故障或挂掉,Kafka将从同步副本列表中选举一个副本为Leader,这个新Leader被选举出来并被接受客户端的消息成功写入。Leader负责维护和跟踪ISR(In-Sync Replicas的缩写,表示副本同步队列)中所有Follower滞后的状态。当Producer发送一条消息到Broker后,Leader写入消息并复制到所有Follower中。消息提交之后才被成功复制到所有的同步副本。消息复制延迟受最慢的Follower限制,对于那些“落后”太多或者失效的Follower,Leader将会把它从ISR中删除。
下面先介绍LEO和HW两个概念,如图所示。

http://www.wjp.ink/wp-content/uploads/2020/12/image-13.png

• LEO:LogEndOffset的缩写,表示每个Partition的log文件中的最后一条消息的位置。
•   HW是HighWatermark的缩写,是指Consumer能够看到的    Partition消息的位置。
Consumer无法消费分区下Leader副本中(Follower)位移值大于分区HW的任何消息(即如上图中6~10部分消息)。这个涉及多副本的概念。

下面通过一个案例说明当Producer生产消息至Broker后,ISR、HW和LEO的流转过程。
(1)初始状态下,HW等于LEO,Follower将Leader中全部消息备份,此时有生产者向Kafka写入消息,如下图所示。

http://www.wjp.ink/wp-content/uploads/2020/12/image-14.png

(2)生产者将消息写入Leader中,此时Leader将变更LEO的位置,Follower1和Follower2将对Leader中的新增消息进行备份,如下图所示

http://www.wjp.ink/wp-content/uploads/2020/12/image-15.png

(3)Follower1完成Leader中所有消息的备份,Follower2未完成备份,此时HW更新为4,如下图所示。

http://www.wjp.ink/wp-content/uploads/2020/12/image-16.png

(4)所有的Follower都将Leader中的消息备份完成,如下图所示。

http://www.wjp.ink/wp-content/uploads/2020/12/image-17.png

一条消息只有被“in sync” list里的所有follower都从leader复制过去才会被认为已提交。这样就避免了部分数据被写进了leader,还没来得及被任何follower复制就宕机了,而造成数据丢失(consumer无法消费这些数据)。
如果一个follower宕机,或者落后太多,leader将把它从”in sync” list中移除。这里所描述的“落后太多”指follower复制的消息落后于leader后的条数超过预定值
Kafka的这种使用“in sync” list的方式则很好的均衡了确保数据不丢失以及吞吐率。follower可以批量的从leader复制数据,这样极大的提高复制性能(批量写磁盘),极大减少了follower与leader的差距(前文有说到,只要follower落后leader不太远,则被认为在“in sync” list里)。

Leader选举机制

        Kafka在Zookeeper中动态维护了一个ISR(in-sync replicas) 集合,这个集合里的所有副本都跟上了leader,只有ISR里的成员才有被选为leader的可能。在这种模式下,对于f+1个副本,一个Kafka topic能在保证不丢失已经commit的消息的前提下容忍f个replica的失败。在大多数使用场景中,这种模式是非常有利的。  上文提到,在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某一个partition的所有副本都挂了,就无法保证数据不丢失了。这种情况下有两种可行的方案:
        在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某个Partition的所有Replica都宕机了,就无法保证数据不丢失了。这种情况下有两种可行的方案:
       1.等待ISR中的任一个Replica“活”过来,并且选它作为Leader
       2.选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader需要在可用性和一致性当中作出一个简单的折衷

在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某个Partition的所有Replica都宕机了,就无法保证数据不丢失了。这种情况下有两种可行的方案:
1.等待ISR中的任一个Replica“活”过来,并且选它作为Leader
2.选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader
需要在可用性和一致性当中作出一个简单的折衷


参考了多篇文章,由于时间关系,今天暂时不贴上来原文地址了,改天抽空找一下原文地址贴上来


参考/转载自:

https://my.oschina.net/u/3777515/blog/3169038
http://www.jasongj.com/2015/01/02/Kafka%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90/
https://blog.csdn.net/tangdong3415/article/details/53432166

wjp

文章作者

发表评论

textsms
account_circle
email

Kafka学习笔记
什么是Kafka Kafka最初是由LinkedIn公司采用Scala语言开发的一个多分区、多副本并且基于ZooKeeper协调的分布式消息系统,现在已经捐献给了Apache基金会。目前Kafka已经定位为一个…
扫描二维码继续阅读
2020-12-21