Kafka(二)架构

一、为了保证kafka的高可用使用的机制

组件

Producer: 生产者,发送消息的一方。生产者负责创建消息,然后将其发送到 Kafka。
Consumer: 消费者,接受消息的一方。消费者连接到 Kafka 上并接收消息,进而进行相应的业务逻辑处理。
Consumer Group: 一个消费者组可以包含一个或多个消费者。使用多分区 + 多消费者方式可以极大提高数据下游的处理速度,同一消费组中的消费者不会重复消费消息,同样的,不同消费组中的消费者消息消息时互不影响。Kafka 就是通过消费组的方式来实现消息 P2P 模式和广播模式。
Broker: 服务代理节点。Broker 是 Kafka 的服务节点,即 Kafka 的服务器。
Topic: Kafka 中的消息以 Topic 为单位进行划分,生产者将消息发送到特定的 Topic,而消费者负责订阅 Topic 的消息并进行消费。
Partition: Topic 是一个逻辑的概念,它可以细分为多个分区,每个分区只属于单个主题。同一个主题下不同分区包含的消息是不同的,分区在存储层面可以看作一个可追加的日志(Log)文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)。
Offset: offset 是消息在分区中的唯一标识,Kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,也就是说,Kafka 保证的是分区有序性而不是主题有序性。
Replication: 副本,是 Kafka 保证数据高可用的方式,Kafka 同一 Partition 的数据可以在多 Broker 上存在多个副本,通常只有主副本对外提供读写服务,当主副本所在 broker 崩溃或发生网络异常,Kafka 会在 Controller 的管理下会重新选择新的 Leader 副本对外提供读写服务。
Record: 实际写入 Kafka 中并可以被读取的消息记录。每个 record 包含了 key、value 和 timestamp。

一、副本机制

1.副本机制
同一个partition可能会有多个replication(对应 server.properties 配置中的 default.replication.factor=N)。
没有replication的情况下,一旦broker 宕机,其上所有 patition 的数据都不可被消费,同时producer也不能再将数据存于其上的patition。引入replication之后,同一个partition可能会有多个replication,而这时需要在这些replication之间选出一个leader,producer和consumer只与这个leader交互,其它replication作为follower从leader 中复制数据。

2.每个分区中的副本中选出Leader
引入Replication之后,同一个Partition可能会有多个Replica,而这时需要在这些Replication之间选出一个Leader,Producer和Consumer只与这个Leader交互,其它Replica作为Follower从Leader中复制数据。
  因为需要保证同一个Partition的多个Replica之间的数据一致性(其中一个宕机后其它Replica必须要能继续服务并且即不能造成数据重复也不能造成数据丢失)。如果没有一个Leader,所有Replica都可同时读/写数据,那就需要保证多个Replica之间互相(N×N条通路)同步数据,数据的一致性和有序性非常难保证,大大增加了Replication实现的复杂性,同时也增加了出现异常的几率。而引入Leader后,只有Leader负责数据读写,Follower只向Leader顺序Fetch数据(N条通路),系统更加简单且高效。
ISR的作用:保证所有副本数据的一致性。数据写入的时候,告知所有的ISR都保存一份
leader挂掉时。Kafka会在ISR中选择一个速度比较快的设为Leader
Controller:集群启动的时候会在Zookeeper中去注册一个controller,所有的ISR会去session文件中抢注为Leader。
Controller的作用:管理所有的Broker,检查Broker的健康状态,节点剔除。针对已经损坏的Broker,检查该Broker中有多少的Leader和Follwer。重新分配之类的事情。

3.将副本部署到集群中的策略
为了更好的做负载均衡,Kafka尽量将所有的Partition均匀分配到整个集群上。一个典型的部署方式是一个Topic的Partition数量大于Broker的数量。同时为了提高Kafka的容错能力,也需要将同一个Partition的Replica尽量分散到不同的机器。实际上,如果所有的Replica都在同一个Broker上,那一旦该Broker宕机,该Partition的所有Replica都无法工作,也就达不到HA的效果。同时,如果某个Broker宕机了,需要保证它上面的负载可以被均匀的分配到其它幸存的所有Broker上。

Kafka分配Replica的算法如下:
1.将所有Broker(假设共n个Broker)和待分配的Partition排序
2.将第i个Partition分配到第(i mod n)个Broker上
3.将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上

二、分区机制

生产者在产生数据 后会将数据推送发布到broker中。这些消息依次被追加到分区中,属于顺序写(顺序写的效率会高于随机写)

Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。
topic是逻辑上(抽象)的概念,而partition是物理上(真实)的概念

通常,不同应用产生不同类型的数据,可以设置不同的主题。一个主题一般会有多个消息的订阅者,当生产者发布消息到某个主题时,订阅了这个主题的消费者都可以接收到生成者写入的新消息。

1.分区
主题的 每个分区都是有序的记录序列,如果有新的消息过来就往后追加
分区方便了服务器在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了。
分区可以提高并发。
2.偏移量
分区中的 消息会按照时间顺序分配一个单调递增的编号,叫偏移量,偏移量可以唯一标识分区中的一条信息。不同分区之间的偏移量都是相互独立的。

3.Partition log
每个Partition中的消息都是有序的,生产的消息被不断追加到Partition log上,其中的每一个消息都被赋予了一个唯一的offset值。
在kafka的设计之初,考虑到了生产者生产的消息不断追加到log文件末尾后导致log文件过大的情况,所以采用了分片和索引机制,具体来说就是将每个partition分为多个segment。每个segment对应三个文件:.index文件、.log文件、.timeindex文件。其中.log和.index文件夹下,该文件夹的命名规则为:topic名称+分区序号。
例如,csdn这个topic有2个分区,则其对应的文件夹为csdn-0,csdn-1;
文件命名规则:partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充。
index 文件并不是从0开始,也不是每次递增1的,这是因为 Kafka采取稀疏索引存储的方式,每隔一定字节的数据建立一条索引,它减少了索引文件大小,使得能够把 index 映射到内存,降低了查询时的磁盘 IO 开销,同时也并没有给查询带来太多的时间消耗。
index文件和log文件的关系:".index"文件存储大量的索引信息,".log"文件存储大量的数据,索引文件中的元数据指向对应数据文件中message的物理偏移地址。
因为每一个segment文件名为上一个Segment最后一条消息的offset,所以当需要查找一个指定offset的message时,通过在所有segment的文件名中进行二分查找就能找到它归属的segment,再在其index文件中找到其对应到文件上的物理位置,就能拿出该message。

4.分区策略(默认)

  1. 如果指定了partition,就写到对应的 分区中
  2. 如果指定了数据的key值,会根据key的值计算出来一个hash值,然后这个值除以分区的个数结果的余数就是要写到哪个分区的分区号
  3. 如果没有执行数据的key值,第一个消息就随机生成 一个数字,后续的数据就在这个随机生成的数字上每个自增1,然后每个消息使用自己的这个数字除以分区的个数结果的余数就是要写到哪个分区的分区号

5.自定义分区
写一个类实现Partitioner接口.然后使用自定义的partition类

三、producer写入消息流程

在这里插入图片描述

  1. producer先从zookeeper的 "/brokers/…/state"节点找到该partition的leader

  2. producer将消息发送给该leader

  3. leader将消息写入本地log

  4. followers从leader pull消息,写入本地log后向leader发送ACK

  5. leader收到所有ISR中的replication的ACK后,增加HW(high watermark,最后commit 的offset)并向producer发送ACK

四、ACK 机制

为保证producer发送的数据,能可靠的发送到指定的topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否则重新发送数据。

AR(All Replica):分区中 所有的副本
ISR(In Sync Replica):同步副本,在规定时间可以实现数据的同步
OSR (Out Sync Replica):在规定的时间内没有实现数据的同步
AR=ISR+OSR

有两个方案对比如下:

  1. 半数以上完成同步,就发送ack(优点:延迟低;缺点:选举新的leader时,容忍n台节点的故障,需要2n+1个副本)
  2. 全部完成同步,才发送ack(优点:选举新的leader时,容忍n台节点的故障,需要n+1个副本;缺点:延迟高)

我kafka采用零拷贝技术优化数据传输,因此网络延迟对kafka的影响较小。但是由于kafka一般都是处理海量数据,在同样为了容忍n台节点故障的前提下,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而Kafka的每个分区都有大量的数据,第一种方案会造成大量数据的冗余,因此kafka采用了第二种方案:全部完成同步,才发送ack。

ack应答机制
kafka处理数据时为了更加灵活,给用户提供了三种可靠性级别,用户可以通过调节acks参数来选择合适的可靠性和延迟。acks的参数分别可以配置为:0,1,-1。

  1. 配置为0:producer不等待broker的ack,这一操作提供了一个最低的延迟,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据;

  2. 配置为1:producer等待broker的ack,partition的leader写入磁盘成功后返回ack,但是如果在follower同步成功之前leader故障,那么将会丢失数据;

  3. 配置为-1:producer等待broker的ack,partition的leader和follower全部写入磁盘成功后才返回ack。但是如果在follower同步完成后,broker发送ack之前,leader发生故障,此时会选举新的leader,但是新的leader已经有了数据,但是由于没有之前的ack,producer会再次发送数据,那么就会造成数据重复。

kafka选用第二种发案来同步副本数据后,可能会出现一个问题:比如leader收到数据,然后开始向所有的follower同步数据,但是有那么一个或多个follower因为挂掉了之类的原因出现了故障,不能和leader进行同步,那leader要一直等下去吗?当然不可以,为了解决这个问题,引入了ISR的概念。ISR是一个动态的in-sync replica set数据集,代表了和leader保持同步的follower集合。
相当于leader只要和ISR里的follower进行数据同步就可以了,出现故障的会被ISR移出去,恢复之后并经过处理还会加入进来。那移出去的follower要经过怎样的处理才能重新加入ISR呢?

五、幂等性机制

Kafka在0.11版本之后,引入了幂等性机制(idempotent),指的是当发送同一条消息时,数据在 Server 端只会被持久化一次,数据不丟不重,但是这里的幂等性是有条件的:

  1. 只能保证 Producer 在单个会话内不丟不重,如果 Producer 出现意外挂掉再重启是 无法保证的。因为幂等性情况下,是无法获取之前的状态信息,因此是无法做到跨会话级别的不丢不重。
  2. 幂等性不能跨多个 Topic-Partition,只能保证单个 Partition 内的幂等性,当涉及多个Topic-Partition 时,这中间的状态并没有同步。

实现exactly once

一般对于重要的数据,我们需要实现数据的精确一致性,对于kafka也就是保证每条消息被发送且仅被发送一次,不能重复,这就是exactly once。知道当acks = -1时,kafka可以实现at least once语义,这时候的数据会被至少发送一次。再配合前面介绍的幂等性机制保证数据不重复,那合在一起就可以实现producer到broker的exactly once语义。它们的关系可以写成一个公式:idempotent + at least once = exactly once。只需将enable.idempotence属性设置为true,kafka会自动将acks属性设为-1。

二、Kafka消费过程

1.Kafka消费者分区分配策略

一个Topic中的数据,由一个Consumer group进行消费
一个Topic中的一个partition,由对应Consumer group中的1个consumer进行消费。
一个Partition分区中的数据,只能被1个Consumer分区消费。(1个partition分区不能同时被一个Consumer group中的多个Consumer消费)

一个consumer group中有多个consumer,一个 topic有多个partition,所以必然会涉及到partition的分配问题,即确定哪个partition由哪个consumer来消费。
Kafka有两种分配策略,一是roundrobin(轮询),一是range(区间、范围)。
1.轮询
在这里插入图片描述
2.区间
在这里插入图片描述

2.HW水位线和offset

由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。
Offset:kafka会保存每个topic数据消费的记录offset,以便记录consumer消费到哪个数据了
Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为consumer_offsets。
HW概念:全称 high watermarker 水位线。
ISR列表中,每个写入对应分区中leader副本的数据,follower会拉取数据期望与leader数据进行同步数据和offset,此刻因为网络延迟,会导致不同的follower拉取的速度不一样,在高并发场景下follower通常会滞后于leader,那么ISR内部offset最低的那个值就是HW。
作用:消费者只能消费HW这个offset以下的数据。
在这里插入图片描述

三、Zookeeper在kafka中的作用

Kafka集群中有一个broker会被选举为Controller(启动broker向zk注册,先到先得),负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。
Controller的管理工作都是依赖于Zookeeper的。

1.注册Broker

Broker是分布式部署并且相互之间相互独立,但是需要有一个注册系统能够将整个集群中的Broker管理起来,此时就使用到了Zookeeper。在Zookeeper上会有一个专门用来进行Broker服务器列表记录的节点:/brokers/ids
Kafka使用了全局唯一的数字来指代每个Broker服务器,不同的Broker必须使用不同的Broker ID进行注册,创建完节点后,每个Broker就会将自己的IP地址和端口信息记录到该节点中去。其中,Broker创建的节点类型是临时节点,一旦Broker宕机,则对应的临时节点也会被自动删除。

2.注册Topic

在Kafka中,同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也都是由Zookeeper在维护,由专门的节点来记录,如:/borkers/topics
Kafka中每个Topic都会以/brokers/topics/[topic]的形式被记录,如/brokers/topics/login和/brokers/topics/search等。Broker服务器启动后,会到对应Topic节点(/brokers/topics)上注册自己的Broker ID并写入针对该Topic的分区总数,如/brokers/topics/login/3->2,这个节点表示Broker ID为3的一个Broker服务器,对于"login"这个Topic的消息,提供了2个分区进行消息存储,同样,这个分区节点也是临时节点。

3、生产者负载均衡

由于同一个Topic消息会被分区并将其分布在多个Broker上,因此,生产者需要将消息合理地发送到这些分布式的Broker上,那么如何实现生产者的负载均衡,Kafka支持传统的四层负载均衡,也支持Zookeeper方式实现负载均衡。

(1) 四层负载均衡,根据生产者的IP地址和端口来为其确定一个相关联的Broker。通常,一个生产者只会对应单个Broker,然后该生产者产生的消息都发往该Broker。这种方式逻辑简单,每个生产者不需要同其他系统建立额外的TCP连接,只需要和Broker维护单个TCP连接即可。但是,其无法做到真正的负载均衡,因为实际系统中的每个生产者产生的消息量及每个Broker的消息存储量都是不一样的,如果有些生产者产生的消息远多于其他生产者的话,那么会导致不同的Broker接收到的消息总数差异巨大,同时,生产者也无法实时感知到Broker的新增和删除。

(2) 使用Zookeeper进行负载均衡,由于每个Broker启动时,都会完成Broker注册过程,生产者会通过该节点的变化来动态地感知到Broker服务器列表的变更,这样就可以实现动态的负载均衡机制。

4、消费者负载均衡

与生产者类似,Kafka中的消费者同样需要进行负载均衡来实现多个消费者合理地从对应的Broker服务器上接收消息,每个消费者分组包含若干消费者,每条消息都只会发送给分组中的一个消费者,不同的消费者分组消费自己特定的Topic下面的消息,互不干扰。

5、分区 与 消费者 的关系

消费组 (Consumer Group):
consumer group 下有多个 Consumer(消费者)。
对于每个消费者组 (Consumer Group),Kafka都会为其分配一个全局唯一的Group ID,Group 内部的所有消费者共享该 ID。订阅的topic下的每个分区只能分配给某个 group 下的一个consumer(当然该分区还可以被分配给其他group)。
同时,Kafka为每个消费者分配一个Consumer ID,通常采用"Hostname:UUID"形式表示。
在Kafka中,规定了每个消息分区 只能被同组的一个消费者进行消费,因此,需要在 Zookeeper 上记录 消息分区 与 Consumer 之间的关系,每个消费者一旦确定了对一个消息分区的消费权力,需要将其Consumer ID 写入到 Zookeeper 对应消息分区的临时节点上,例如:
/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]
其中,[broker_id-partition_id]就是一个 消息分区 的标识,节点内容就是该 消息分区 上 消费者的Consumer ID。

6、消息 消费进度Offset 记录

在消费者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度Offset记录到Zookeeper上,以便在该消费者进行重启或者其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。Offset在Zookeeper中由一个专门节点进行记录,其节点路径为:/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]。节点内容就是Offset的值。

7、消费者注册

消费者服务器在初始化启动时加入消费者分组的步骤如下

  1. 注册到消费者分组。每个消费者服务器启动时,都会到Zookeeper的指定节点下创建一个属于自己的消费者节点,例如/consumers/[group_id]/ids/[consumer_id],完成节点创建后,消费者就会将自己订阅的Topic信息写入该临时节点。

  2. 对 消费者分组 中的 消费者 的变化注册监听。每个 消费者 都需要关注所属 消费者分组 中其他消费者服务器的变化情况,即对/consumers/[group_id]/ids节点注册子节点变化的Watcher监听,一旦发现消费者新增或减少,就触发消费者的负载均衡。

  3. 对Broker服务器变化注册监听。消费者需要对/broker/ids/[0-N]中的节点进行监听,如果发现Broker服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者负载均衡。

  4. 进行消费者负载均衡。为了让同一个Topic下不同分区的消息尽量均衡地被多个 消费者 消费而进行 消费者 与 消息 分区分配的过程,通常,对于一个消费者分组,如果组内的消费者服务器发生变更或Broker服务器发生变更,会发出消费者负载均衡。

链接:https://www.jianshu.com/p/a036405f989c

8.从broker中选出一个承担KafkaController的职责

防止单点故障,也是临时节点`
负责在需要的时候对ISR列表中选出leader

9.保存Topic的元数据信息(描述信息)

zookeeper保存topic的分区和leader信息,并协助KafkaController在需要的时候(启动或者broker故障)选出新的分区的leader
kafkaController会监听 zookeeper中的borkers下的ids节点的子节点变化

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值