1. Log的读写、删除流程---日志管理器(log manager)负责创建日志、获取日志、清理日志。所有的日志读写操作都交给具体的日志实例来完成。

KafkaServer启动的时候,初始化三个类:

  • LogManager
  • SocketServer
  • the request handlers - KafkaRequestHandlers

按照顺序组件有:

  1. KafkaScheduler
  2. LogManager
  3. SocketServer
  4. ReplicaManager
  5. KafkaController
  6. GroupCoordinator
  7. KafkaApis
  8. KafkaRequestHandlerPool
  9. KafkaHealthcheck

Tips:

  1. KafkaServer启动的时候,用到了数据结构CountDownLatch shutdownLatch: 用作以后KafkaServer stop的时候,等待别的都关闭完成。
// After calling shutdown(), use this API to wait until the shutdown is complete.
def awaitShutdown(): Unit = shutdownLatch.await()

1.2 LogManager startup

  1. 遍历所有的日志目录,如果有.kafka_cleanshutdown就说明是干净的shutdown,跳过当前log的recovery
  2. 读取recovery-point-offset-checkpoint文件,返回(topic, partition)->offset 映射。其中文件的第一行是版本号,第二行是记录条数,后面是映射内容
  3. 计算出log目录和对应的recovery point,并用来初始化Log对象。
  4. 清理工作,清理掉.kafka_cleanshutdown
  5. 启动三个定时任务,kafka-log-retention, kafka-log-flusher, kafka-recovery-point-checkpoint
    • retention是删除时间过久,体积过大的log文件。
    • flusher是根据时间间隔刷盘,调用FileChannel.force。
    • recovery-point-checkpoint是把内存里的(topic, partition)->offset checkpoint结构刷盘。
  6. 启动LogCleaner

1.2.1 一些删除的策略

  1. 删除掉.deleted和.cleaned后缀的文件
  2. 碰到.swap说明在swap中途server挂掉,这里的swap过程仅仅是一个rename。这种情况对于index的swap直接删除后面从log文件重建,如果是log的swap就先删index再通过rename的方式swap回log
  3. 遇到.index索引文件,如果没有对应的.log文件直接删除
  4. 通过.log文件生成LogSegment对象,如果没有索引文件就运行LogSegment.recover重建索引
  5. 重建索引的过程是以message为单位遍历LogSegment,遍历过程中如果累加的大小超过了index interval就在索引里面记录一下当前消息偏移量(会转化成相对偏移)和.log文件里面的字节为单位的位置。这个过程中会顺便把log文件和index文件末尾的可能因为crash产生的多余的字节给清理掉。
  6. 开始根据recovery point做恢复,这里碰到cleanShutdown文件就会跳过恢复
  7. 遍历从recovery point到末尾的LogSegment,调用LogSegment.recover,如果过程中发现多余的字符说明LogSegment非法,就把非法的一直到末尾的全部删掉。
  8. 最后对所有的index做一遍sanityCheck

1.2.2 Log.append

  1. 参数是MessageSet,一次append一批消息
  2. 分析校验,拿到是否递增,压缩codec,以及校验大小,crc,干掉多余的字节
  3. 后面的所有过程都加上Log级别的锁,如果需要设置偏移量,就遍历每条消息,设置上偏移量
  4. 再做一次消息大小检查,因为前面可能重新压缩过消息
  5. 如果当前LogSegment大小加上要append的大小超过上限就滚动到新的segment
  6. 用当前Log的logEndOffset生成新的log file和index file,之前的末尾index文件做一下裁剪
  7. 用新的log file和index file生成新的LogSegment并加入Log里面的列表,并且把recoveryPoint到之前的logEndOffset的segment加入刷盘队列,刷完了以后recoveryPoint增长到logEndOffset
  8. 如果增长的字节量达到了需要做索引的长度就在index里面append一个entry,然后FileMessageSet.append
  9. 如果append以后没刷盘的字节过多就刷一下盘

1.2.3 Log.read

  1. 参数有一个startOffset,首先定位到offset仅仅比这个低的entry,如果定位不到就直接异常
  2. 调用LogSegment.read,首先要找到log文件里面对应startOffset的第一个合法的position
  3. 查找过程先通过index查找offset对应的position,查找的过程是标准二分查找,因为用了mmap所以都是在内存里面找。index因为需要经常的随机读和append操作,所以做了mmap
  4. 调用FileMessageSet.searchFor,在log文件里找大于offset的合法message的position
  5. 用同样的方法计算出end offset对应position,在算出需要读取的字节数
  6. 通过这些信息,返回一个FileMessageSet,这里还没有实际读取,后面会提取出byte message返回

1.2.4 LogCleaner

  1. 如果对一个消息存在key相同但是offset更高的消息,那么就清理掉这个offset低的消息
  2. 先找最脏的Log,找的方法是先读取cleaner-offset-checkpoint里面存了每个log的最后清理offset,然后每个log拿最后清理的offset到active offset所有segments的size和log size算一个比率,然后取比率最大的
  3. 开始清理Log,先遍历所有脏的LogSegment,构建一个内存中的OffsetMap(index),key是消息key,value是消息offset,这个map后面用来判断消息是否是重复的
  4. 把所有的LogSegment分个组,每个组的字节大小总量尽量接近于某个指定值。尽量把一个组压缩成一个新的Segment,这样做让新的Segment体积尽量平均
  5. 拿出每个组的第一个segment以它的名字创建log和index对应的.cleaned文件
  6. 对于组里的每个LogSegment,过滤掉重复的和空的message,其余的全部写入那个新的目标LogSegment
  7. 新的LogSegment刷盘,然后把旧的segment list全部删除,最后把.cleaned名字改成.log文件

2.网络协议

看看请求的网络协议里面都存了写什么。

2.1 request and response

  1. 首先解析2字节的requestId
  2. 根据requestId选择kafka.api包里面对应的request类来处理,序列化反序列化协议 包含在对应的request类里面
  3. 有一个correlationId做request和response的关联
  4. 拿ProducerRequest做例子
+----------------------------+
| requestId 2 |
+----------------------------+
| versionId 2 |
+----------------------------+
| correlationId 4 |
+----------------------------+
| clientId |
+----------------------------+
| requiredAcks 2 |
+----------------------------+
| ackTimeoutMs 4 |
+----------------------------+
| topicCount 4 |
+----------------------------+
| +-------------------------+|
| | topic ||
| +-------------------------+|
| | partitionCount 4 ||
| +-------------------------+|
| | +----------------------+||
| | | partition 4 |||
| | +----------------------+||
| | | messageSetSize 4 |||
| | +----------------------+||
| | | messageSetContent |||
| | | |||
| | +----------------------+||
| +-------------------------+|
+----------------------------+

message的内容

+--------------+
|CRC 4 |
+--------------+
|Magic 1 |
+--------------+
|attributes 1 |
+--------------+
|key 4 |
+--------------+
|size 4 |
+--------------+
|content |
+--------------+

第一次创建topic的最后会启动highWaterMark的checkPoint线程,这个线程定期刷所有replica的checkPoint到磁盘。落地在磁盘的文件名叫做:replication-offset-checkpoint

3.partition reassign 扩容的机制

PartitionsReassignedListener监听/admin/reassign_partitions,由KafkaController控制---KafkaController.onPartitionReassignment 。扩容时,需要停止Producer和Consumer。

4.高可用

4.1 high water mark

  1. high water mark通常是一个partition所有replica的endOffset的最小值,也是同步提交模式情况下集群commit的位置
  2. 读的时候客户端最多只能读到highWatermark的位置
  3. 一个follower挂掉重启的时候首先扔掉它自己highWatermark之后的数据,然后开始追赶leader
  4. leader挂掉的会重新选,新leader把自己的endOffset当做新的highWatermark,然后让其它的replica开始追赶

4.2 in sync replica

  1. Partition会启动一个超时线程,调用Partition.maybeShrinkIsr,检查所有isr的end offset和最后更新时间,差距过大的就踢掉
  2. 处理follower的fetch request的时候可能会触发Partition.updateLeaderHWAndMaybeExpandIsr,重新检查这个replica的各种条件,可能的话就加回到isr

4.3 partition leader election

  1. PartitionLeaderSelector.OfflinePartitionLeaderSelector是用得最多的leader selector,选leader的逻辑是从controller context里面拿出live isr和live replica,优先选择live isr的第一个,如果没有就选择live replica的第一个,否则就抛异常
  2. 更新zk path /brokers/topics/huying_test1/partitions/0/state
  3. 给这个partition的每个replica broker发一个请求LeaderAndIsrRequest,其中leader和follower的指令稍有区别。然后给所有活着的broker发一个请求UpdateMetadataRequest。这里的请求都是异步带callback(多数情况是null)的
  4. 发送请求如果失败会重试,终止重试的流程是zk那边发起,导致当前发送线程被关闭
  5. 接收到请求的broker开始ReplicaManager.makeLeaders, makeFollowers相关的流程
  6. 处理UpdateMetadataRequest请求比较简单,更新一下cache数据就ok了
  7. 触发点,KafkaController.onBrokerStartup, KafkaController.onBrokerFailure分别对应broker启动的时候和broker挂掉的时候,KafkaController.onPreferredReplicaElection,使用了工具:kafka-preferred-replica-election.sh

4.4 controller leader election

  1. controller是kafka全局的leader,controller负责监听zookeeper然后分析状态变更发送命令到其它的broker
  2. broker启动的时候开始监听/controller节点,这里注意一个zk的bug(session失效可能过了一段时间临时节点才会删除,如果碰到node exist exception就返回可能会都选不上) LeaderChangeListener可能会触发KafkaController.onControllerFailover或者KafkaController.onControllerResignation
  3. onControllerFailover首先会读取zk上的epoch,然后+1乐观锁方式更新zk,epoch用来标识正确的leader,当新leader产生旧leader没挂掉的时候可以作废掉旧leader的命令,broker里面关注epoch的manager保存一份epoch,接到比自己大的epoch就设定成新的
  4. 监听几个zk path,/admin/reassign_partitions,/admin/preferred_replica_election,/brokers/topics,/admin/delete_topics,/brokers/ids,/brokers/topics/topicXXX
  5. 初始化controllerContext相关的内存结构和一些manager,并给其它的broker发一个metadata更新的命令
  6. 如果配了自动平衡partition leader,就启动一个检查并自动平衡partition leader的scheduler,调用checkAndTriggerPartitionRebalance,不平衡率高的时候触发onPreferredReplicaElection

    7.onControllerResignation执行相反的过程,取消注册之前的几个zk listener并且关闭几个manager

5.broker启动和关闭

  1. broker启动的时候注册SessionExpireListener,这里调用的是zkClient.subscribeStateChanges(sessionExpireListener)而不是监听zk临时节点,感觉碰到机器假死会少了删临时节点的手段。然后注册临时节点/brokers/ids/xxx,这个临时节点会被controller的BrokerChangeListener监听
  2. KafkaController.onBrokerStartup 首先给新启动的broker发送metadata
  3. 给新broker上的所有replica触发OnlineReplica的状态变更,会触发broker上面的replicaManager.becomeLeaderOrFollower,具体的逻辑和前面创建topic里面描述的相同
  4. 触发partitionStateMachine的状态变更,可能会导致leader重新选举,同时也会触发broker上面的replicaManager.becomeLeaderOrFollower
  5. 如果在新的broker上有partition reassignment,就调用onPartitionReassignment
  6. broker关闭的逻辑类似,也是触发partitionStateMachine和replicaStateMachine的状态变化,稍有不同的是那些leader在这台broker上的partition要先触发一个Offline再触发一个Online

6.高性能

  1. 磁盘顺序写速度很快,pagecache的存在加速读写
  2. 攒消息成message set,网络上传大包,更大块的磁盘操作,内存连续性更好
  3. 协议固定,消息字节在broker,consumer,producer之间传输不需要修改
  4. 字节从pagecache到socket在linux可以通过sendfile优化

2 Kafka Broker的更多相关文章

  1. kafka broker 进入 conflicted ephemeral node 死循环

    转载请注明原创地址 http://www.cnblogs.com/dongxiao-yang/p/5621303.html 最近发现kafka一台服务器producer客户端写入时一直报错,查看该br ...

  2. 关于Kafka broker IO的讨论

    Apache Kafka是大量使用磁盘和页缓存(page cache)的,特别是对page cache的应用被视为是Kafka实现高吞吐量的重要因素之一.实际场景中用户调整page cache的手段并 ...

  3. Kafka broker配置介绍 (四)

    这部分内容对了解系统和提高软件性能都有很大的帮助,kafka官网上也给出了比较详细的配置详单,但是我们还是直接从代码来看broker到底有哪些配置需要我们去了解的,配置都有英文注释,所以每一部分是干什 ...

  4. Kafka 单节点多Kafka Broker集群

    Kafka 单节点多Kafka Broker集群 接前一篇文章,今天搭建一下单节点多Kafka Broker集群环境. 配置与启动服务 由于是在一个节点上启动多个 Kafka Broker实例,所以我 ...

  5. Kafka 单节点单Kafka Broker集群

    下载与安装 从 http://www.apache.org/dist/kafka/ 下载最新版本的 kafka,这里使用的是 kafka_2.12-0.10.2.1.tgz $ tar zxvf ka ...

  6. spark streaming 接收kafka消息之三 -- kafka broker 如何处理 fetch 请求

    首先看一下 KafkaServer 这个类的声明: Represents the lifecycle of a single Kafka broker. Handles all functionali ...

  7. kafka broker Leader -1引起spark Streaming不能消费的故障解决方法

    一.问题描述:Kafka生产集群中有一台机器cdh-003由于物理故障原因挂掉了,并且系统起不来了,使得线上的spark Streaming实时任务不能正常消费,重启实时任务都不行.查看kafka t ...

  8. Kafka Broker | 命令行选项和过程

    1.目标 在这个Apache Kafka教程中,我们将学习Kafka Broker.Kafka Broker管理主题中的消息存储.如果Apache Kafka有多个代理,那就是我们所说的Kafka集群 ...

  9. Spark Streaming + Kafka整合(Kafka broker版本0.8.2.1+)

    这篇博客是基于Spark Streaming整合Kafka-0.8.2.1官方文档. 本文主要讲解了Spark Streaming如何从Kafka接收数据.Spark Streaming从Kafka接 ...

  10. Structured Streaming + Kafka Integration Guide 结构化流+Kafka集成指南 (Kafka broker version 0.10.0 or higher)

    用于Kafka 0.10的结构化流集成从Kafka读取数据并将数据写入到Kafka. 1. Linking 对于使用SBT/Maven项目定义的Scala/Java应用程序,用以下工件artifact ...

随机推荐

  1. Leetcode题库——5.最长回文子串

    @author: ZZQ @software: PyCharm @file: longestPalindrome.py @time: 2018/9/18 20:06 要求:给定一个字符串 s,找到 s ...

  2. dazhewang数据库初设计

    mysql> use dazhe; Database changed mysql> create table shops(id int primary key auto_increment ...

  3. centos7开机出现try again to boot into default maintenance give root password for maintenance

    开启centos7出现下面两句话,然后直接输出root密码,就可以登录,但是登录后,发现一些文字显示出来的是乱码 try again to boot into default maintenanceg ...

  4. JabRef学习笔记(一)

    JabRef简介 JabRef is an open source bibliography reference manager. The native file format used by Jab ...

  5. Spring Cloud之Eureka服务注册与发现

    解决什么问题 ➟阐述微服务以及服务注册发现的部分概念 ➟阐述Eureka服务注册与发现的部分原理及细节 为什么需要服务中心 过去,每个应用都是一个CPU,一个主机上的单一系统.然而今天,随着大数据和云 ...

  6. sublime Text 插件收录

    插件 1.SublimeText3常用快捷键和优秀插件 2.常用的sublime text 3插件-1 主题 1.https://www.jianshu.com/p/1a1113213faf 2.ht ...

  7. c# 连接操作linux

    0.背景 现在linux重要性是显然易见的,学习linux是必须,通过程序来来控制linux 也能发挥很大的作用.比如我们可以做一个自动化部署的程序,来发布程序到linux上面. 1.在项目中添加SS ...

  8. userData Behavior

    https://msdn.microsoft.com/zh-cn/vstudio/ms531424 userData Behavior This topic documents a feature o ...

  9. JVM内存管理机制

    Java与C++之间有一堆由内存动态分配与垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人却想出来. —— <深入理解Java虚拟机:JVM高级特性与最佳实践> Java虚拟机在 ...

  10. 2018 桂林ccpc现场赛 总结

    Day 0 5个小时的火车,坐的昏昏欲睡.桂林站出来没有地铁,而是出租车排成长队依次上车,也算是某种意义上的地铁吧.到了酒店才发现学校那边又给我们换了,又拖着行李找新的酒店,途中路过一家餐馆,所有人都 ...