跟着锋哥学Java

深入浅出Kafka(四)之Kafka Broker

1.1Zookeeper 存储的 Kafka 信息

   1.如何查看

1)启动 Zookeeper 客户端。
[atguigu@hadoop102 zookeeper-3.5.7]$ bin/zkCli.sh
(2)通过 ls 命令可以查看 kafka 相关信息。

   2.在zookeeper的服务端存储的Kafka相关信息

   1)/kafka/brokers/ids [0,1,2] 记录有哪些服务器,在配置文件中配置的那个 broker.id这个就用于唯一标识kafka的一个服务实例,kafka启动的时候,会将这个信息上报zk
   2)/kafka/brokers/topics/first/partitions/0/state {"leader":1 ,"isr":[1,0,2] }记录了first中,0号分区下谁是Leader,有哪些服务器可用
   3)/kafka/controller{“brokerid”:0}辅助选举Leader
     4)/kafka/consumers 0.9版本之前用于保存offset信息,0.9版本之后offset存储在kafka主题中
     5)其他信息
admin,里面维护着删除的topic信息
brokers,维护着kafkabroker相关的节点信息,包括topic、topic中的分区信息等
cluster,集群元信息
consumers,0.9版本之前用于保存offset信息,0.9版本之后offset存储在kafka主题中
controller,维护着集群中leader信息
config,存储集群配置相关的信息

 1.2 Broker 重要参数

   1.replica.lag.time.max.ms: ISR 中,如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。该时间阈值,默认 30s。

    2.auto.leader.rebalance.enable  默认是 true。 自动 Leader Partition 平衡。

    3.leader.imbalance.per.broker.percentage  默认是 10%。每个 broker 允许的不平衡的 leader的比率。如果每个 broker 超过了这个值,控制器会触发 leader 的平衡。

    4.leader.imbalance.check.interval.seconds 默认值 300 秒。检查 leader 负载是否平衡的间隔时间。

    5.log.segment.bytes:  Kafka 中 log 日志是分成一块块存储的,此配置是指 log 日志划分 成块的大小,默认值 1G。

    6.log.index.interval.bytes:默认 4kb,kafka 里面每当写入了 4kb 大小的日志(.log),然后就往 index 文件里面记录一个索引。

    7.log.retention.hour  Kafka 中数据保存的时间,默认 7 天。

    8.log.retention.minutes: Kafka 中数据保存的时间,分钟级别,默认关闭。
 
    9.log.retention.ms:  Kafka 中数据保存的时间,毫秒级别,默认关闭。
 
   10.og.retention.check.interval.ms:检查数据是否保存超时的间隔,默认是 5 分钟。
 
   11.log.retention.bytes: 默认等于-1,表示无穷大。超过设置的所有日志总大小,删除最早的 segment。
 
   12.log.cleanup.policy: 默认是 delete,表示所有数据启用删除策略;如果设置值为 compact,表示所有数据启用压缩策略。
 
   13.num.io.threads: 默认是 8。负责写磁盘的线程数。整个参数值要占总核数的 50%。
 
   14.num.replica.fetchers:副本拉取线程数,这个参数占总核数的 50%的 1/3
 
   15.num.network.threads:默认是 3。数据传输线程数,这个参数占总核数的50%的 2/3 。
 
   16.log.flush.interval.messages:强制页缓存刷写到磁盘的条数,默认是 long 的最大值,9223372036854775807。一般不建议修改,交给系统自己管理。
 
   17.log.flush.interval.ms: 每隔多久,刷数据到磁盘,默认是 null。一般不建议修改,交给系统自己管理

2.Kafka 副本

   2.1 副本基本信息

    1)Kafka 副本作用:提高数据可靠性。

     2)Kafka 默认副本 1 个,生产环境一般配置为 2 个,保证数据可靠性太多副本会增加磁盘存储空间,增加网络上数据传输,降低效率。
     3)Kafka 中副本分为:Leader 和 Follower。Kafka 生产者只会把数据发往 Leader,然后 Follower 找 Leader 进行同步数据
     4)Kafka 分区中的所有副本统称为 AR(Assigned Repllicas)AR = ISR + OSR
   ISR,表示和 Leader 保持同步的 Follower 集合如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。该时间阈值由 replica.lag.time.max.ms参数设定,默认 30s。Leader 发生故障之后,就会从 ISR 中选举新的 Leader。
    OSR,表示 Follower 与 Leader 副本同步时,延迟过多的副本
      5)副本进入ISR列表有两个条件:
   1. 副本节点不能产生分区,必须能与zookeeper保持会话以及跟leader副本网络连通
   2. 副本能复制leader上的所有写操作,并且不能落后太多。(如果 Follower 长时间未向 Leader 发送通信请求或同步数据,则该 Follower 将被踢出 ISR。该时间阈值由 replica.lag.time.max.ms参数设定,默认 30s)

   2.2Leader 选举流程

       2.2.1Kafka核心总控制器Controller

          1.在Kafka集群中会有一个或者多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态,同时负责管理集群broker 的上下线,所有 topic 的分区副本分配和 Leader 选举等工作

           2.当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。

          3.当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。
          4.当使用kafka-topics.sh脚本为某个topic增加分区数量时,同样还是由控制器负责让新分区被其他节点感知到
          5. Controller 的信息同步工作是依赖于 Zookeeper 的;

    2.2.1Kafka核心总控制器Controller选举机制

         1.在kafka集群启动的时候,会自动选举一台broker作为controller来管理整个集群,选举的过程是集群中每个broker都会尝试在zookeeper上创建一个 /controller 临时节点.zookeeper会保证有且仅有一个broker能创建成功,这个broker就会成为集群的总控器controller

         2.当这个controller角色的broker宕机了,此时zookeeper临时节点会消失,集群里其他broker会一直监听这个临时节点,发现临时节点消失了,就竞争再次创建临时节点,就是我们上面说的选举机制,zookeeper又会保证有一个broker成为新的controller。

         3.具备控制器身份的broker需要比其他普通的broker多一份职责,例如下:

   1) 监听broker相关的变化。为Zookeeper中的/brokers/ids/节点添加BrokerChangeListener,用来处理broker增减的变化。
   2)监听topic相关的变化。为Zookeeper中的/brokers/topics节点添加TopicChangeListener,用来处理topic增减的变化;为Zookeeper中的/admin/delete_topics节点添加TopicDeletionListener,用来处理删除topic的动作。
           3)从Zookeeper中读取获取当前所有与topic、partition以及broker有关的信息并进行相应的管理。对于所有topic所对应的Zookeeper中的/brokers/topics/[topic]节点添加PartitionModificationsListener,用来监听topic中的分区分配变化。
          4)更新集群的元数据信息,同步到其他普通的broker节点中

     2.2.3 副本Leader 选举流程

     1)核心总控制器Controller选举成功之后,开始选择Leader副本

     2)由选举出来的Controller监听brokers节点变化 ->(/kafka/brokers/ids [0,1,2]),同时决定Leader的选举.

     3)选举规则:在isr中存活为前提,按照AR中排在前面的优先。例如ar[1,0,2],isr [1,0,2],那么leader就会按照1,0,2的顺序轮询.

    4)Controller将节点副本的Leader和Follower信息上传到ZK->(/brokers/topics/first/partitions/0/state"leader"1: ,"isr":[1,0,2 ])

    5)其他contorller从zk同步Leader和Follower等相关信息

     6)假设Broker1中Leader挂了,这时由选举出来的Controller监听brokers节点变化.同时拉取ISR存活节点副本数

    7)重新选举新的Leader,并且更新Leader及ISR

     

  2.3Leader 和 Follower 故障处理细节

    2.3.1HW与LEO

     1.LEO(Log End Offset):每个副本的最后一个offset,LEO其实就是最新的offset + 1

       2.HW俗称高水位,HighWatermark的缩写,取一个partition对应的ISR中最小的LEO(log-end-offset)作为HW,也就是所有副本中最小的LEO.保证消费者消费数据的一致性。

       

    3.consumer最多只能消费到HW所在的位置

    4.每个replica都有HW,leader和follower各自负责更新自己的HW的状态;

    5.leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步后更新HW,此时消息才能被consumer消费

    6.这样就保证了如果leader所在的broker失效,该消息仍然可以从新选举的leader中获取。对于来自内部broker的读取请求,没有HW的限制

   2.3.2ISR以及HW和LEO的流转过程

     1.Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。事实上,同步复制要求所有能工作的follower都复制完,这条消息才会被commit,这种复制方式极大的影响了吞吐率。

    2.而异步复制方式下,follower异步的从leader复制数据,数据只要被leader写入log就被认为已经commit,这种情况下如果follower都还没有复制完,落后于leader时,突然leader宕机,则会丢失数据。而Kafka的这种使用ISR的方式则很好的均衡了确保数据不丢失以及吞吐率.

  3.流转过程

   4.结合HW和LEO看下 acks=1的情况

       

 2.3.3Leader 和 Follower 故障处理细节

    1.Follower故障

         

   2.Follower故障说明:

(1) Follower发生故障后会被临时踢出ISR, 这个期间Leader和其他的Follower继续接收数据
(2)待该Follower恢复后,Follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向Leader进行同步。
(3)等该Follower的LEO大于等于该Partition的HW,即该Follower追上Leader之后,就可以重新加入ISR了。

   3.Leader故障

 

  4.Leader故障说明

  (1) Leader发生故障之后,会从ISR中选出一个新的Leader

  (2)为保证多个副本之间的数据一致性,其余的Follower会先将各自的log文件高于HW的部分截掉,然后从新的Leader同步数据。

    注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。

2. 3. 生产经验——Leader Partition 负载平衡

        1. 正常情况下,Kafka本身会自动把Leader Partition均匀分散在各个机器上,来保证每台机器的读写吞吐量都是均匀的。但是如果某些broker宕机,会导致Leader Partition过于集中在其他少部分几台broker上,这会导致少数几台broker的读写请求压力过高,其他宕机的broker重启之后都是follower partition,读写请求很低,造成集群负载不均衡

   

 2.下面拿一个主题举例说明,假设集群只有一个主题如下图所示:

针对broker0节点,分区2的AR优先副本是0节点,但是0节点却不是Leader节点,所以不平衡数加1,AR副本总数是4,所以broker0节点不平衡率为1/4>10%,需要再平衡。
broker2和broker3节点和broker0不平衡率一样,需要再平衡。Broker1的不平衡数为0,不需要再平衡 

3.参数:

 1.auto.leader.rebalance.enable,默认是true。 自动Leader Partition 平衡,生产建议关闭或者允许的不平衡的leader的比率设置大一些, 否则会消耗大量服务器的性能

  2.leader.imbalance.per.broker.percentage,默认是10%。每个broker允许的不平衡的leader的比率。如果每个broker超过了这个值,控制器会触发leader的平衡。
  3.leader.imbalance.check.interval.seconds,默认值300秒。检查leader负载是否平衡的间隔时间

3. 文件存储机制

 3.1Topic 数据的存储机制

    1.Topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是Producer生产的数据

    2.Producer生产的数据会被不断追加到该log文件末端,为防止log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment

    3.Kafka 一个分区的消息数据对应存储在一个文件夹下,以topic名称+分区号命名,消息在分区内是分段(segment)存储,每个segment包括:“.index”文件、“.log”文件和.timeindex等文件
    4.Kafka Broker 有一个参数,log.segment.bytes,限定了每个日志段(segment)文件的大小,最大就是 1GB。
    5.一个日志段文件满了,就自动开一个新的日志段文件来写入,避免单个文件过大,影响文件的读写性能,这个过程叫做log rolling,正在被写入的那个日志段文件,叫做 active log segment。 
    

 

 

     
 
 
 
 
 
 
 
   总结: 
     1)一个topic分为多个partition,一个partition分为多个segment,每个segment文件的大小,最大就是 1GB,每个segment文件包括:“.index”文件、“.log”文件和.timeindex等文件
     2).log 日志文件,主要存offset和消息体 
     3)index 偏移量索引文件,部分消息的offset索引文件,kafka每次往分区发4K(可配置)消息就会记录一条当前消息的offset到index文件(index为稀疏索引,大约每往log文件写入4kb数据,会往index文件写入一条索引)
     4)timeindex 时间戳索引文件 ,也就是消息的发送时间索引文件,kafka每次往分区发4K(可配置)消息就会记录一条当前消息的发送时间戳与对应的offset到timeindex文件,如果需要按照时间来定位消息的offset,会先在这个文件里查找 
     5)说明:index和log文件以当前segment的第一条消息的offset命名。
    6).Index文件中保存的offset为相对offset,这样能确保offset的值所占空间不会过大,因此能将offset的值控制在固定大小

  3.2数据的查找过程

  1.根据目标offset定位Segment文件
  2.找到小于等于目标offset的最大offset对应的索引项,定位到log文件
  3.向下遍历找到目标Record
     

  3.3日志存储参数配置

    1.log.segment.bytes:Kafka 中 log 日志是分成一块块存储的,此配置是指 log 日志划分成块的大小,默认值 1G。

    2.log.index.interval.bytes:默认 4kb,kafka 里面每当写入了 4kb 大小的日志(.log),然后就往 index 文件里面记录一个索引。 稀疏索引。

3.4文件清理策略

   1.Kafka 中默认的日志保存时间为 7 天.

   2.Kafka 中提供的日志清理策略有 delete 和 compact 两种。

 3.4.1清理策略的参数配置

  1.log.retention.hours:小时,默认 7 天。
  2.log.retention.minutes:分钟。
  3.log.retention.ms:毫秒。
       4.前三种参数说明:优先级->如果配置了毫秒参数,则其他分钟与小时参数无效,如果配置了分钟参数,小时参数无效;
  5.log.retention.check.interval.ms:负责设置检查周期,默认 5 分钟
       6.log.cleanup.policy = delete 或者compact: 清理策略配置 ,默认为delete
       7.log.retention.bytes:在清理策略为delete时,基于Segment大小删除时,用于设置segment日志大小,默认等于-1,表示无穷大

  3.4.2文件清理策略之delete

       1.delete 日志删除:将过期数据删除 ;

       2.基于时间:默认打开。以 segment 中所有记录中的最大时间戳作为该文件时间戳;

       3.基于大小:默认关闭。超过设置的所有日志总大小(可配置),删除最早的 segment(不推荐).

        

       4.问题1:如果一个 segment 中有一部分数据过期,一部分没有过期,怎么处理? 

       5.问题1解决方案:数据过期的就等着,等着日志文件中,最大的时间戳对应数据过期后一起删除。

 3.4.2文件清理策略之compact

      1.compact日志压缩:对于相同key的不同value值,只保留最后一个版本

      2.这种策略只适合特殊场景,比如消息的key是用户ID,value是用户的资料,通过这种压缩策略,整个消息集里就保存了所有用户最新的资料

      

     3.压缩后的offset可能是不连续的,比如上图中没有6,当从这些offset消费消息时,将会拿到比这个offset大的offset对应的消息,实际上会拿到offset为7的消息,并从这个位置开始消费

  3.5高效读写数据

 1.Kafka 本身是分布式集群,可以采用分区技术,并行度高
 2.读数据采用稀疏索引,可以快速定位要消费的数据
 3.顺序写磁盘 
   4.页缓存 + 零拷贝技术

   3.5.1顺序写磁盘

      1.kafka消息不能修改以及不会从文件中间删除保证了磁盘顺序读,kafka的消息写入文件都是追加在文件末尾,不会写入文件中的某个位置(随机写)保证了磁盘顺序写
      2.Kafka 的 producer 生产数据,要写入到 log 文件中,写的过程是一直追加到文件末端,为顺序写。
      3.官网有数据表明,同样的磁盘,顺序写能到 600M/s,而随机写只有 100K/s。这与磁盘的机械机构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间。
        

  3.5.2页缓存 + 零拷贝技术

      1.零拷贝:Kafka的数据加工处理操作交由Kafka生产者和Kafka消费者处理。Kafka Broker应用层不关心存储的数据,所以就不用走应用层,传输效率高

       2.PageCache页缓存:Kafka重度依赖底层操作系统提供的PageCache功 能。当上层有写操作时,操作系统只是将数据写入PageCache。当读操作发生时,先从PageCache中查找,如果找不到,再去磁盘中读取。实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用

    3.5.3参数配置

     1.log.flush.interval.messages:强制页缓存刷写到磁盘的条数,默认是 long 的最大值,9223372036854775807。一般不建议修改,交给系统自己管理。

     2.log.flush.interval.ms:每隔多久,刷数据到磁盘,默认是null。一般不建议修改,交给系统自己管理

posted on 2022-04-30 17:57  跟着锋哥学Java  阅读(1517)  评论(0编辑  收藏  举报

导航