Kafka相爱相杀第1集一理论基础

一、kafka系统介绍

Kafka属于Apache组织,是一个高性能跨语言分布式发布订阅消息队列系统,采用Scala和Java语言编写,它提供了快速、可扩展的、分布式、分区的和可复制的日志订阅服务。它由Producer(消息生产者)、Broker(一个kafka消息接收、存储、分发到消费者 的节点)、Consumer(消息消费者)三部分构成。它的主要特点有:

以时间复杂度O(1)的方式提供消息持久化能力,并对大数据量能保证常数时间的访问性能;
高吞吐率,单台服务器可以达到每秒几十万的吞吐速率;
支持服务器间的消息分区,支持分布式消费,同时保证了每个分区内的消息顺序;
轻量级,支持实时数据处理和离线数据处理两种方式。

1.1 主要功能

根据官网的介绍,Apache Kafka是一个分布式流媒体平台,它主要有3种功能:

  • 1:发布和订阅消息流,这个功能类似于消息队列,这也是kafka归类为消息队列框架的原因

  • 2:以容错的方式记录消息流,kafka以文件的方式来存储消息流

  • 3:可以在消息发布的时候进行处理

1.2 Kafka的特性

  • 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。
  • 可扩展性:kafka集群支持热扩展
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
  • 容错性:允许集群中节点失败(若副本数量为2n+1,则允许n个节点失败)
  • 高并发:支持数千个客户端同时读写

1.3 使用场景

  • 1:在系统或应用程序之间构建可靠的用于传输实时数据的管道,消息队列功能

  • 2:构建实时的流数据处理程序来变换或处理数据流,数据处理功能

二、角色介绍

1.broker
物理层面的机器节点。一台机器上部署了kafka,那么这台机器就是kafka集群的一个节点。一般情况下,一台机器只会部署一个kafka。

2.topic
用于消息分组的最基础的抽象概念。一个集群或某一个broker都可以容纳有多个topic。

3.partition
基于topic的分组,每一个topic又可以分为多个partition。partition之间没有关联,各自有各自的顺序和消息内容。俗称topic分区。

4.producer

生产者。发送消息到kafka的角色。需要指定topic。也可以指定partition。当不指定partition的时候,也可以根据自己的需求实现partition的“负载均衡”。 创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性。

在这里插入图片描述
在每个分区中,消息以顺序存储,最晚接收的的消息会最后被消费。

kafka中的message以topic的形式存在,topic在物理上又分为很多的partition,partition物理上由很多segment组成,segment是存放message的真正载体。

5.consumer

消费者。从kafka消费消息的角色。必须隶属于一个consumer group。

6.offset

记录consumer消费消息进度。consumer-topic-partition-offset。

7.consumer group

消费组。用于区分消费者们。

  • 一个consumer group可以有多个consumer,但是一个consumer只能属于一个consumer group。

  • 消费组与消费组之间无任何关联,可以消费相同的消息,offset也互相都不关联。

  • 一个consumer group可以订阅多个topic。

  • 每一个topic下的partition只能被一个consumer消费,不过可以被不同consumer group下的多个consumer消费。

  • 一个consumer group里的一个consumer都能消费多个partition,但是同组的其他consumer不能同时消费一个partition。

  • 一个consumer group里consumer数量最好小于等于topic下的partition数量,否则会有consumer被闲置。

8. partitioner
消息分区器,用于获取当前消息在指定topic下面的具体分区。

三、kafka生产者

话不多说,先上图,(此图摘自:https://www.jianshu.com/p/7feea4860a0f)在这里插入图片描述
首先,创建ProducerRecord必须包含Topic和Value,key和partition可选。然后,序列化key和value对象为ByteArray,并发送到网络。

接下来,消息发送到partitioner。如果创建ProducerRecord时指定了partition,此时partitioner啥也不用做,简单的返回指定的partition即可。如果未指定partition,partitioner会基于ProducerRecord的key生成partition。producer选择好partition后,增加record到对应topic和partition的batch record。最后,专有线程负责发送batch record到合适的Kafka broker。

当broker收到消息时,它会返回一个应答(response)。如果消息成功写入Kafka,broker将返回RecordMetadata对象(包含topic,partition和offset);相反,broker将返回error。这时producer收到error会尝试重试发送消息几次,直到producer返回error。

实例化producer后,接着发送消息。这里主要有3种发送消息的方法:

  • 立即发送:只管发送消息到server端,不care消息是否成功发送。大部分情况下,这种发送方式会成功,因为Kafka自身具有高可用性,producer会自动重试;但有时也会丢失消息;

  • 同步发送:通过send()方法发送消息,并返回Future对象。get()方法会等待Future对象,看send()方法是否成功;

  • 异步发送:通过带有回调函数的send()方法发送消息,当producer收到Kafka broker的response会触发回调函数

以上所有情况,一定要时刻考虑发送消息可能会失败,想清楚如何去处理异常。

通常我们是一个producer起一个线程开始发送消息。为了优化producer的性能,一般会有下面几种方式:单个producer起多个线程发送消息;使用多个producer。

消息均衡策略

在这里插入图片描述

生产者在向kafka集群发送消息的时候,可以通过指定分区来发送到指定的分区中

也可以通过指定均衡策略来将消息发送到不同的分区中

如果不指定,就会采用默认的随机均衡策略,将消息随机的存储到不同的分区中

四、kafka消费者

4.1 消费模式

kafka的消费模式总共有3种:最多一次,最少一次,正好一次。为什么会有这3种模式,是因为客户端处理消息,提交反馈(commit)这两个动作不是原子性。

  • 1.最多一次:客户端收到消息后,在处理消息前自动提交,这样kafka就认为consumer已经消费过了,偏移量增加。
  • 2.最少一次:客户端收到消息,处理消息,再提交反馈。这样就可能出现消息处理完了,在提交反馈前,网络中断或者程序挂了,那么kafka认为这个消息还没有被consumer消费,产生重复消息推送。
  • 3.正好一次:保证消息处理和提交反馈在同一个事务中,即有原子性。

本文从这几个点出发,详细阐述了如何实现以上三种方式。

At-most-once(最多一次)
设置enable.auto.commit为ture
设置 auto.commit.interval.ms为一个较小的时间间隔.
client不要调用commitSync(),kafka在特定的时间间隔内自动提交。

At-least-once(最少一次)
方法一
设置enable.auto.commit为false
client调用commitSync(),增加消息偏移;

方法二
设置enable.auto.commit为ture
设置 auto.commit.interval.ms为一个较大的时间间隔.
client调用commitSync(),增加消息偏移;

Exactly-once(正好一次)
3.1 思路
如果要实现这种方式,必须自己控制消息的offset,自己记录一下当前的offset,对消息的处理和offset的移动必须保持在同一个事务中,例如在同一个事务中,把消息处理的结果存到mysql数据库同时更新此时的消息的偏移。
3.2 实现
设置enable.auto.commit为false
保存ConsumerRecord中的offset到数据库
当partition分区发生变化的时候需要rebalance,有以下几个事件会触发分区变化
1 consumer订阅的topic中的分区大小发生变化
2 topic被创建或者被删除
3 consuer所在group中有个成员挂了
4 新的consumer通过调用join加入了group
此时 consumer通过实现ConsumerRebalanceListener接口,捕捉这些事件,对偏移量进行处理。
consumer通过调用seek(TopicPartition, long)方法,移动到指定的分区的偏移位置。

4.2 消费组

在这里插入图片描述

在消费者消费消息时,kafka使用offset来记录当前消费的位置

在kafka的设计中,可以有多个不同的group来同时消费同一个topic下的消息,如图,我们有两个不同的group同时消费,他们的的消费的记录位置offset各不项目,不互相干扰。

对于一个group而言,消费者的数量不应该多余分区的数量,因为在一个group中,每个分区至多只能绑定到一个消费者上,即一个消费者可以消费多个分区,一个分区只能给一个消费者消费

因此,若一个group中的消费者数量大于分区数量的话,多余的消费者将不会收到任何消息。

4.3 分区重平衡

在这里插入图片描述

可以看到,当新的消费者加入消费组,它会消费一个或多个分区,而这些分区之前是由其他消费者负责的;另外,当消费者离开消费组(比如重启、宕机等)时,它所消费的分区会分配给其他分区。这种现象称为重平衡(rebalance)。重平衡是Kafka一个很重要的性质,这个性质保证了高可用和水平扩展。不过也需要注意到,在重平衡期间,所有消费者都不能消费消息,因此会造成整个消费组短暂的不可用。而且,将分区进行重平衡也会导致原来的消费者状态过期,从而导致消费者需要重新更新状态,这段期间也会降低消费性能。后面我们会讨论如何安全的进行重平衡以及如何尽可能避免。

消费者通过定期发送心跳(hearbeat)到一个作为组协调者(group coordinator)的broker来保持在消费组内存活。这个broker不是固定的,每个消费组都可能不同。当消费者拉取消息或者提交时,便会发送心跳。

如果消费者超过一定时间没有发送心跳,那么它的会话(session)就会过期,组协调者会认为该消费者已经宕机,然后触发重平衡。可以看到,从消费者宕机到会话过期是有一定时间的,这段时间内该消费者的分区都不能进行消息消费;通常情况下,我们可以进行优雅关闭,这样消费者会发送离开的消息到组协调者,这样组协调者可以立即进行重平衡而不需要等待会话过期。

在0.10.1版本,Kafka对心跳机制进行了修改,将发送心跳与拉取消息进行分离,这样使得发送心跳的频率不受拉取的频率影响。另外更高版本的Kafka支持配置一个消费者多长时间不拉取消息但仍然保持存活,这个配置可以避免活锁(livelock)。活锁,是指应用没有故障但是由于某些原因不能进一步消费。

五、kafka 消息存储方式

Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。
Segment:partition物理上由多个segment组成
offset:每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中. partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息.
topic中partition存储分布
在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。

├── data0
│   ├── cleaner-offset-checkpoint  //存了每个log的最后清理offset
│   ├── client_mblogduration-35  //名为client_mblogduration的topic的35号分区的数据
│   │   ├── 00000000000004909731.index
│   │   ├── 00000000000004909731.log        // 1G文件--Segment
│   │   ├── 00000000000005048975.index      // 数字是Offset
│   │   ├── 00000000000005048975.log
│   ├── client_mblogduration-37 //名为client_mblogduration的topic的37号分区的数据
│   │   ├── 00000000000004955629.index
│   │   ├── 00000000000004955629.log
│   │   ├── 00000000000005098290.index
│   │   ├── 00000000000005098290.log
│   ├── __consumer_offsets-33
│   │   ├── 00000000000000105157.index
│   │   └── 00000000000000105157.log
│   ├── meta.properties    //broker.id 信息
│   ├── recovery-point-offset-checkpoint  //表示已经刷写到磁盘的记录。recoveryPoint以下的数据都是已经刷 到磁盘上的了。
│   └── replication-offset-checkpoint  //用来存储每个replica的HighWatermark的(high watermark (HW),表示已经被commited的message,HW以下的数据都是各个replicas间同步的,一致的。)

partiton中文件存储方式

每个partion(目录)由多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除。

每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。

segment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件.
segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。

segment中index<—->data file对应关系物理结构如下:

index与log映射关系

在这里插入图片描述
.index文件存放的是message逻辑相对偏移量(相对offset=绝对offset-base offset)与在相应的.log文件中的物理位置(position)。但.index并不是为每条message都指定到物理位置的映射,而是以entry为单位,每条entry可以指定连续n条消息的物理位置映射(例如:假设有20000~20009共10条消息,.index文件可配置为每条entry
指定连续10条消息的物理位置映射,该例中,index entry会记录偏移量为20000的消息到其物理文件位置,一旦该条消息被定位,20001~20009可以很快查到。)。每个entry大小8字节,前4个字节是这个message相对于该log segment第一个消息offset(base offset)的相对偏移量,后4个字节是这个消息在log文件中的物理位置。

Kafka高效文件存储设计特点

  • topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
  • 通过索引信息可以快速定位message和确定response的最大大小。
  • 通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
  • 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。

参考:https://www.cnblogs.com/byrhuangqiang/p/6364088.html

六、 Kafka集群副本容灾原理

如下图所示:

在这里插入图片描述

6.1 Kafka的副本机制简介

​ 上图展示了一个名为topic1,分区数为3、副本数为3的话题的数据在机器数量为4的Kafka集群上的存储方式。因为分区数为3,所以消息被分为part0、part1、part3这三个区。因为副本数是3,所以每一个分区里面的数据都在集群里面存储了3份。默认创建topic的时候分区数和副本数都是1 。

​ 上图可知,当前集群中的1号机器是该话题的0号分区的leader节点,则当该话题的Producer和Consumer在生产和消费0号分区的消息的时候都直接与1号节点交互,1号节点会将0号分区的相关信息同步到2号节点和3号节点中。此时如果1号节点故障,则:2号节点或3号节点会被选举为0号分区新的leader与Producer和Consumer交互,而1号分区和2号分区的交互不会受到任何影响,从而实现了集群容灾。

​ 由上可知,Kafka是通过的副本的方式实现容灾。假设创建一个名为topic2,分区数为3、副本数为1的话题。则该话题数据的存储方式,就会类似于上图中去掉所有绿色框之后的样子。即。每个分区的数据都只存储一份。此时2号节点就是该话题的1号分区的数据的存储机器,其它机器上都没有1号分区的数据,如果此时2号节点故障,则该话题的整个数据存储和消费都会受到影响,应为其他机器上都没有1号分区的副本数据,无法恢复故障的机器数据。

​ 在通常情况下,增加分区可以提供kafka集群的吞吐量。然而,也应该意识到集群的总分区数或是单台服务器上的分区数过多,会增加不可用及延迟的风险。

6.2 Kafka创建副本的2种模式—同步复制和异步复制

Kafka动态维护了一个同步状态的副本的集合(a set of In-Sync Replicas),简称ISR,在这个集合中的节点都是和leader保持高度一致的,任何一条消息只有被这个集合中的每个节点读取并追加到日志中,才会向外部通知说“这个消息已经被提交”。

只有当消息被所有的副本加入到日志中时,才算是“committed”,只有committed的消息才会发送给consumer,这样就不用担心一旦leader down掉了消息会丢失。

消息从leader复制到follower, 我们可以通过决定Producer是否等待消息被提交的通知(ack)来区分同步复制和异步复制。

同步复制流程:

​ 1.producer联系zk识别leader

​ 2.向leader发送消息

​ 3.leadr收到消息写入到本地log

​ 4.follower从leader pull消息

​ 5.follower向本地写入log

​ 6.follower向leader发送ack消息

​ 7.leader收到所有follower的ack消息

​ 8.leader向producer回传ack

​ 异步复制流程:

​ 1.producer联系zk识别leader

​ 2.向leader发送消息

​ 3.leadr收到消息写入到本地log,leader向producer回传ack

​ 4.follower从leader pull消息

​ 5.follower向本地写入log

​ 6.follower向leader发送ack消息

和同步复制的区别在于:异步复制时,leader写入本地log之后, 直接向client回传ack消息,不需要等待所有follower复制完成。

由于Kafka支持副本模式,所以当其中一个Broker挂掉,一个新的leader就能通过ISR机制推选出来,继续处理读写请求。

6.3 broker节点存活条件

Kafka集群判断一个节点是否存活,需要满足已下2个条件:

    1. 节点必须可以维护和ZooKeeper的连接,Zookeeper通过心跳机制检查每个节点的连接。
    1. 如果节点是个follower,他必须能及时的同步leader的写操作,延时不能太久。

​ Leader会追踪所有“同步中”的节点,一旦一个down掉了,或是卡住了,或是延时太久,leader就会把它移除.

6.3 Kafka集群的容灾能力

首先,必须要明确:

  • Kafka集群的leader选举是通过zookeeper实现的,而zookeeper的选举策略是需要 半数以上 的节点参与选举,才能产生新的leader。

eg1:假如有一个3个机器的集群,当leader机器故障之后,剩下的2个机器因为大于总数的一半,所以可以在他两中选举产生一个新的leader。此时新的leader节点再故障的话,剩下的1个正常节点无法独自选举出新的leader节点,因为没有超过总数的一半机器参与选举。此时容错能力为1。

eg2:假如有一个4个机器的集群,当leader机器故障之后,剩下的3个机器因为大于总数的一半,所以可以在他们3个中选举产生一个新的leader。此时新的leader节点再故障的话,剩下的2个正常节点无法独自选举出新的leader节点,因为没有超过总数的一半机器参与选举。此时容错能力为1。

以上2个例子说明,Kafka集群的2n+1和2n+2个机器的leader容灾能力是一样的,都是n。出于节约资源的考虑,大家部署集群的时候,都是部署奇数个机器。

eg3:假如有一个5个机器的集群,当leaderA机器故障之后,剩下的4个机器因为大于总数的一半,所以可以在他们4个中选举产生一个新的leaderB。此时新的leaderB节点再故障的话,剩下的3个正常节点还是大于集群总节点的一半,所以可以再次选举产生一个新的leaderC。此时新的leaderC节点再故障的话,剩下的2个正常节点无法独自选举出新的leader节点,因为没有超过总数的一半机器参与选举。此时容错能力为2。

上面说的都是leader节点故障的情况,但是当故障的全是flower节点时,情况就完全不一样了。因为flower节点即使故障了,也不会影响整个集群的正常工作,所以允许全部的flower节点都故障。

eg4:假如有一个5个机器的集群,只要leader节点正常,即使其他4个flower节点都故障了集群也是可以正常工作的。此时容错能力为4!

即:当故障的节点全是flower的时候,可以达到最大的容错能力n-1。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值