Kafka 是最后由 Linkedin 公司开发,是一个分布式、反对分区的(partition)、多正本的(replica),基于 zookeeper 协调的分布式音讯零碎,它的最大的个性就是能够实时的解决大量数据以满足各种需要场景:比方基于 hadoop 的批处理零碎、低提早的实时零碎、storm/Spark 流式解决引擎,web/nginx 日志、拜访日志,音讯服务等等,用 scala 语言编写,Linkedin 于 2010 年奉献给了 Apache 基金会并成为顶级开源我的项目。
1. 前言
音讯队列的性能好坏,其文件存储机制设计是掂量一个音讯队列服务技术水平和最要害指标之一。上面将从 Kafka 文件存储机制和物理构造角度,剖析 Kafka 是如何实现高效文件存储,及理论利用成果。
1.1 Kafka 的个性:
- 高吞吐量、低提早:kafka 每秒能够解决几十万条音讯,它的提早最低只有几毫秒,每个 topic 能够分多个 partition, consumer group 对 partition 进行 consume 操作。
- 可扩展性:kafka 集群反对热扩大
- 持久性、可靠性:音讯被长久化到本地磁盘,并且反对数据备份避免数据失落
- 容错性:容许集群中节点失败(若正本数量为 n, 则容许 n - 1 个节点失败)
- 高并发:反对数千个客户端同时读写
1.2 Kafka 的应用场景:
- 日志收集:一个公司能够用 Kafka 能够收集各种服务的 log,通过 kafka 以对立接口服务的形式凋谢给各种 consumer,例如 hadoop、Hbase、Solr 等。
- 音讯零碎:解耦和生产者和消费者、缓存音讯等。
- 用户流动跟踪:Kafka 常常被用来记录 web 用户或者 app 用户的各种流动,如浏览网页、搜寻、点击等流动,这些流动信息被各个服务器公布到 kafka 的 topic 中,而后订阅者通过订阅这些 topic 来做实时的监控剖析,或者装载到 hadoop、数据仓库中做离线剖析和开掘。
- 经营指标:Kafka 也常常用来记录经营监控数据。包含收集各种分布式应用的数据,生产各种操作的集中反馈,比方报警和报告。
- 流式解决:比方 spark streaming 和 storm
1.3 Kakfa 的设计思维
- Kakfa Broker Leader 的选举:Kakfa Broker 集群受 Zookeeper 治理。所有的 Kafka Broker 节点一起去 Zookeeper 上注册一个长期节点,因为只有一个 Kafka Broker 会注册胜利,其余的都会失败,所以这个胜利在 Zookeeper 上注册长期节点的这个 Kafka Broker 会成为 Kafka Broker Controller,其余的 Kafka broker 叫 Kafka Broker follower。(这个过程叫 Controller 在 ZooKeeper 注册 Watch)。这个 Controller 会监听其余的 Kafka Broker 的所有信息,如果这个 kafka broker controller 宕机了,在 zookeeper 下面的那个长期节点就会隐没,此时所有的 kafka broker 又会一起去 Zookeeper 上注册一个长期节点,因为只有一个 Kafka Broker 会注册胜利,其余的都会失败,所以这个胜利在 Zookeeper 上注册长期节点的这个 Kafka Broker 会成为 Kafka Broker Controller,其余的 Kafka broker 叫 Kafka Broker follower。例如:一旦有一个 broker 宕机了,这个 kafka broker controller 会读取该宕机 broker 上所有的 partition 在 zookeeper 上的状态,并选取 ISR 列表中的一个 replica 作为 partition leader(如果 ISR 列表中的 replica 全挂,选一个幸存的 replica 作为 leader; 如果该 partition 的所有的 replica 都宕机了,则将新的 leader 设置为 -1,期待复原,期待 ISR 中的任一个 Replica“活”过去,并且选它作为 Leader;或抉择第一个“活”过去的 Replica(不肯定是 ISR 中的)作为 Leader),这个 broker 宕机的事件,kafka controller 也会告诉 zookeeper,zookeeper 就会告诉其余的 kafka broker。
- Consumergroup:各个 consumer(consumer 线程)能够组成一个组(Consumer group),partition 中的每个 message 只能被组(Consumer group)中的一个 consumer(consumer 线程)生产,如果一个 message 能够被多个 consumer(consumer 线程)生产的话,那么这些 consumer 必须在不同的组。Kafka 不反对一个 partition 中的 message 由两个或两个以上的同一个 consumer group 下的 consumer thread 来解决,除非再启动一个新的 consumer group。所以如果想同时对一个 topic 做生产的话,启动多个 consumer group 就能够了,然而要留神的是,这里的多个 consumer 的生产都必须是程序读取 partition 外面的 message,新启动的 consumer 默认从 partition 队列最头端最新的中央开始阻塞的读 message。当启动一个 consumer group 去生产一个 topic 的时候,无论 topic 外面有多个少个 partition,无论咱们 consumer group 外面配置了多少个 consumer thread,这个 consumer group 上面的所有 consumer thread 肯定会生产全副的 partition;即使这个 consumer group 下只有一个 consumer thread,那么这个 consumer thread 也会去生产所有的 partition。因而,最优的设计就是,consumer group 下的 consumer thread 的数量等于 partition 数量,这样效率是最高的。同一 partition 的一条 message 只能被同一个 Consumer Group 内的一个 Consumer 生产。不可能一个 consumer group 的多个 consumer 同时生产一个 partition。
- Consumer Rebalance 的触发条件:(1): Consumer 减少或删除会触发 Consumer Group 的 Rebalance(2)Broker 的减少或者缩小都会触发 Consumer Rebalance。
- Consumer:Consumer 解决 partition 外面的 message 的时候是 o(1)程序读取的。所以必须保护着上一次读到哪里的 offsite 信息。high level API,offset 存于 Zookeeper 中,low level API 的 offset 由本人保护。一般来说都是应用 high level api 的。Consumer 的 delivery gurarantee,默认是读完 message 先 commmit 再解决 message,autocommit 默认是 true,这时候先 commit 就会更新 offsite+1,一旦解决失败,offsite 曾经 +1,这个时候就会丢 message;也能够配置成读完音讯解决再 commit,这种状况下 consumer 端的响应就会比较慢的,须要等解决完才行。如果 producer 的流量增大,以后的 topic 的 parition 数量 =consumer 数量,这时候的应答形式就是很想扩大:减少 topic 下的 partition,同时减少这个 consumer group 下的 consumer。
- Delivery Mode: Kafka producer 发送 message 不必保护 message 的 offsite 信息,因为这个时候,offsite 就相当于一个自增 id,producer 就只管发送 message 就好了。然而 Consumer 端是须要保护这个 partition 以后生产到哪个 message 的 offsite 信息的,这个 offsite 信息,high level api 是保护在 Zookeeper 上,low level api 是本人的程序保护。当应用 high level api 的时候,先拿 message 解决,再定时主动 commit offsite+1(也能够改成手动), 并且 kakfa 解决 message 是没有锁操作的。因而如果解决 message 失败,此时还没有 commit offsite+1,当 consumer thread 重启后会反复生产这个 message。然而作为高吞吐量高并发的实时处理零碎,at least once 的状况下,至多一次会被解决到,是能够容忍的。如果无奈容忍,就得应用 low level api 来本人程序保护这个 offsite 信息,那么想什么时候 commit offsite+ 1 就本人搞定了。
- Topic & Partition:Topic 相当于传统音讯零碎 MQ 中的一个队列 queue,producer 端发送的 message 必须指定是发送到哪个 topic,然而不须要指定 topic 下的哪个 partition,因为 kafka 会把收到的 message 进行 load balance,平均的散布在这个 topic 下的不同的 partition 上(hash(message) % [broker 数量])。在物理构造上,每个 partition 对应一个物理的目录(文件夹),文件夹命名是[topicname]_[partition]_[序号],一个 topic 能够有有数多的 partition,依据业务需要和数据量来设置。在 kafka 配置文件中可随时更高 num.partitions 参数来配置更改 topic 的 partition 数量,在创立 Topic 时通过参数指定 parittion 数量。Topic 创立之后通过 Kafka 提供的工具也能够批改 partiton 数量。一般来说,(1)一个 Topic 的 Partition 数量大于等于 Broker 的数量,能够进步吞吐率。(2)同一个 Partition 的 Replica 尽量扩散到不同的机器,高可用。当 add a new partition 的时候,partition 外面的 message 不会从新进行调配,原来的 partition 外面的 message 数据不会变,新加的这个 partition 刚开始是空的,随后进入这个 topic 的 message 就会从新参加所有 partition 的 load balance。
- Partition Replica:每个 partition 能够在其余的 kafka broker 节点上存正本,以便某个 kafka broker 节点宕机不会影响这个 kafka 集群。存 replica 正本的形式是依照 kafka broker 的程序存。例如有 5 个 kafka broker 节点,某个 topic 有 3 个 partition,每个 partition 存 2 个正本,那么 partition1 存 broker1,broker2,partition2 存 broker2,broker3。。。以此类推(replica 正本数目不能大于 kafka broker 节点的数目,否则报错。这里的 replica 数其实就是 partition 的正本总数,其中包含一个 leader,其余的就是 copy 正本)。这样如果某个 broker 宕机,其实整个 kafka 内数据仍然是残缺的。然而,replica 正本数越高,零碎尽管越稳固,然而回来带资源和性能上的降落;replica 正本少的话,也会造成零碎丢数据的危险。
(1)怎么传送音讯:producer 先把 message 发送到 partition leader,再由 leader 发送给其余 partition follower。
(2)在向 Producer 发送 ACK 前须要保障有多少个 Replica 曾经收到该音讯:依据 ack 配的个数而定。
(3)怎么解决某个 Replica 不工作的状况:如果这个部工作的 partition replica 不在 ack 列表中,就是 producer 在发送音讯到 partition leader 上,partition leader 向 partition follower 发送 message 没有响应而已,这个不会影响整个零碎,也不会有什么问题。如果这个不工作的 partition replica 在 ack 列表中的话,producer 发送的 message 的时候会期待这个不工作的 partition replca 写 message 胜利,然而会等到 time out,而后返回失败因为某个 ack 列表中的 partition replica 没有响应,此时 kafka 会主动的把这个部工作的 partition replica 从 ack 列表中移除,当前的 producer 发送 message 的时候就不会有这个 ack 列表下的这个部工作的 partition replica 了。
(4)怎么解决 Failed Replica 复原回来的状况:如果这个 partition replica 之前不在 ack 列表中,那么启动后从新受 Zookeeper 治理即可,之后 producer 发送 message 的时候,partition leader 会持续发送 message 到这个 partition follower 上。如果这个 partition replica 之前在 ack 列表中,此时重启后,须要把这个 partition replica 再手动加到 ack 列表中。(ack 列表是手动增加的,呈现某个部工作的 partition replica 的时候主动从 ack 列表中移除的) - Partition leader 与 follower:partition 也有 leader 和 follower 之分。leader 是主 partition,producer 写 kafka 的时候先写 partition leader,再由 partition leader push 给其余的 partition follower。partition leader 与 follower 的信息受 Zookeeper 管制,一旦 partition leader 所在的 broker 节点宕机,zookeeper 会冲其余的 broker 的 partition follower 上抉择 follower 变为 parition leader。
- Topic 调配 partition 和 partition replica 的算法:(1)将 Broker(size=n)和待调配的 Partition 排序。(2)将第 i 个 Partition 调配到第(i%n)个 Broker 上。(3)将第 i 个 Partition 的第 j 个 Replica 调配到第((i + j) % n)个 Broker 上。
- Partition ack:当 ack=1,示意 producer 写 partition leader 胜利后,broker 就返回胜利,无论其余的 partition follower 是否写胜利。当 ack=2,示意 producer 写 partition leader 和其余一个 follower 胜利的时候,broker 就返回胜利,无论其余的 partition follower 是否写胜利。当 ack=-1[parition 的数量]的时候,示意只有 producer 全副写胜利的时候,才算胜利,kafka broker 才返回胜利信息。这里须要留神的是,如果 ack= 1 的时候,一旦有个 broker 宕机导致 partition 的 follower 和 leader 切换,会导致丢数据。
- message 状态:在 Kafka 中,音讯的状态被保留在 consumer 中,broker 不会关怀哪个音讯被生产了被谁生产了,只记录一个 offset 值(指向 partition 中下一个要被生产的音讯地位),这就意味着如果 consumer 解决不好的话,broker 上的一个音讯可能会被生产屡次。
- message 长久化:Kafka 中会把音讯长久化到本地文件系统中,并且放弃 o(1)极高的效率。咱们家喻户晓 IO 读取是十分耗资源的性能也是最慢的,这就是为了数据库的瓶颈常常在 IO 上,须要换 SSD 硬盘的起因。然而 Kafka 作为吞吐量极高的 MQ,却能够十分高效的 message 长久化到文件。这是因为 Kafka 是程序写入 o(1)的工夫复杂度,速度十分快。也是高吞吐量的起因。因为 message 的写入长久化是程序写入的,因而 message 在被生产的时候也是按程序被生产的,保障 partition 的 message 是程序生产的。个别的机器, 单机每秒 100k 条数据。
- message 有效期:不同的版本不一样,目前默认保留 7 天。
- Produer:Producer 向 Topic 发送 message,不须要指定 partition,间接发送就好了。kafka 通过 partition ack 来管制是否发送胜利并把信息返回给 producer,producer 能够有任意多的 thread,这些 kafka 服务器端是不 care 的。Producer 端的 delivery guarantee 默认是 At least once 的。也能够设置 Producer 异步发送实现 At most once。Producer 能够用主键幂等性实现 Exactly once。
- Kafka 高吞吐量: Kafka 的高吞吐量体现在读写上,分布式并发的读和写都十分快,写的性能体现在以 o(1)的工夫复杂度进行程序写入。读的性能体现在以 o(1)的工夫复杂度进行程序读取,对 topic 进行 partition 分区,consume group 中的 consume 线程能够以很高能性能进行程序读。
- Kafka delivery guarantee(message 传送保障):(1)At most once 音讯可能会丢,相对不会反复传输;(2)At least once 音讯相对不会丢,然而可能会反复传输;(3)Exactly once 每条信息必定会被传输一次且仅传输一次,这是用户想要的。
- 冗余: replica 有多个正本,保障一个 broker node 宕机后不会影响整个服务。
- 扩展性: broker 节点能够程度扩大,partition 也能够程度减少,partition replica 也能够程度减少。
- 峰值: 在访问量剧增的状况下,kafka 程度扩大, 利用依然须要持续发挥作用。
- 可恢复性: 零碎的一部分组件生效时,因为有 partition 的 replica 正本,不会影响到整个零碎。
- 程序保障性:因为 kafka 的 producer 的写 message 与 consumer 去读 message 都是程序的读写,保障了高效的性能。
- 缓冲:因为 producer 那面可能业务很简略,而后端 consumer 业务会很简单并有数据库的操作,因而必定是 producer 会比 consumer 处理速度快,如果没有 kafka,producer 间接调用 consumer,那么就会造成整个零碎的处理速度慢,加一层 kafka 作为 MQ,能够起到缓冲的作用。
- 异步通信:作为 MQ,Producer 与 Consumer 异步通信。
2:kafka 一些原理概念
- 长久化:kafka 应用文件存储音讯 (append only log), 这就间接决定 kafka 在性能上重大依赖文件系统的自身个性. 且无论任何 OS 下, 对文件系统自身的优化是十分艰巨的. 文件缓存 / 间接内存映射等是罕用的伎俩. 因为 kafka 是对日志文件进行 append 操作, 因而磁盘检索的开销是较小的; 同时为了缩小磁盘写入的次数,broker 会将音讯临时 buffer 起来, 当音讯的个数(或尺寸) 达到肯定阀值时, 再 flush 到磁盘, 这样缩小了磁盘 IO 调用的次数. 对于 kafka 而言, 较高性能的磁盘, 将会带来更加间接的性能晋升.
- 性能:除磁盘 IO 之外, 咱们还须要思考网络 IO, 这间接关系到 kafka 的吞吐量问题.kafka 并没有提供太多高超的技巧; 对于 producer 端, 能够将音讯 buffer 起来, 当音讯的条数达到肯定阀值时, 批量发送给 broker; 对于 consumer 端也是一样, 批量 fetch 多条音讯. 不过音讯量的大小能够通过配置文件来指定. 对于 kafka broker 端, 仿佛有个 sendfile 零碎调用能够潜在的晋升网络 IO 的性能: 将文件的数据映射到零碎内存中,socket 间接读取相应的内存区域即可, 而无需过程再次 copy 和替换 (这里波及到 ” 磁盘 IO 数据 ”/” 内核内存 ”/” 过程内存 ”/” 网络缓冲区 ”, 多者之间的数据 copy)。
其实对于 producer/consumer/broker 三者而言,CPU 的开销应该都不大, 因而启用消息压缩机制是一个良好的策略; 压缩须要耗费大量的 CPU 资源, 不过对于 kafka 而言, 网络 IO 更应该须要思考. 能够将任何在网络上传输的音讯都通过压缩.kafka 反对 gzip/snappy 等多种压缩形式 - 负载平衡:kafka 集群中的任何一个 broker, 都能够向 producer 提供 metadata 信息, 这些 metadata 中蕴含 ” 集群中存活的 servers 列表 ”/”partitions leader 列表 ” 等信息 (请参看 zookeeper 中的节点信息). 当 producer 获取到 metadata 信息之后, producer 将会和 Topic 下所有 partition leader 放弃 socket 连贯; 音讯由 producer 间接通过 socket 发送到 broker, 两头不会通过任何 ” 路由层 ”.
异步发送,将多条音讯暂且在客户端 buffer 起来, 并将他们批量发送到 broker; 小数据 IO 太多, 会拖慢整体的网络提早, 批量提早发送事实上晋升了网络效率; 不过这也有肯定的隐患, 比方当 producer 生效时, 那些尚未发送的音讯将会失落。 - Topic 模型:其余 JMS 实现, 音讯生产的地位是有 prodiver 保留, 以便防止反复发送音讯或者将没有生产胜利的音讯重发等, 同时还要管制音讯的状态. 这就要求 JMS broker 须要太多额定的工作. 在 kafka 中,partition 中的音讯只有一个 consumer 在生产, 且不存在音讯状态的管制, 也没有简单的音讯确认机制, 可见 kafka broker 端是相当轻量级的. 当音讯被 consumer 接管之后,consumer 能够在本地保留最初音讯的 offset, 并间歇性的向 zookeeper 注册 offset. 由此可见,consumer 客户端也很轻量级。
kafka 中 consumer 负责保护音讯的生产记录, 而 broker 则不关怀这些, 这种设计不仅进步了 consumer 端的灵活性, 也适度的加重了 broker 端设计的复杂度; 这是和泛滥 JMS prodiver 的区别. 此外,kafka 中音讯 ACK 的设计也和 JMS 有很大不同,kafka 中的音讯是批量 (通常以音讯的条数或者 chunk 的尺寸为单位) 发送给 consumer, 当音讯生产胜利后, 向 zookeeper 提交音讯的 offset, 而不会向 broker 交付 ACK. 或者你曾经意识到, 这种 ” 宽松 ” 的设计, 将会有 ” 失落 ” 音讯 /” 音讯重发 ” 的危险。 - 音讯传输统一:Kafka 提供 3 种音讯传输一致性语义:最多 1 次,起码 1 次,恰好 1 次。
起码 1 次:可能会重传数据,有可能呈现数据被反复解决的状况;
最多 1 次:可能会呈现数据失落状况;
恰好 1 次:并不是指真正只传输 1 次,只不过有一个机制。确保不会呈现“数据被反复解决”和“数据失落”的状况。
at most once: 消费者 fetch 音讯, 而后保留 offset, 而后解决音讯; 当 client 保留 offset 之后, 然而在音讯处理过程中 consumer 过程生效(crash), 导致局部音讯未能持续解决. 那么尔后可能其余 consumer 会接管, 然而因为 offset 曾经提前保留, 那么新的 consumer 将不能 fetch 到 offset 之前的音讯(只管它们尚没有被解决), 这就是 ”at most once”.
at least once: 消费者 fetch 音讯, 而后解决音讯, 而后保留 offset. 如果音讯解决胜利之后, 然而在保留 offset 阶段 zookeeper 异样或者 consumer 生效, 导致保留 offset 操作未能执行胜利, 这就导致接下来再次 fetch 时可能取得上次曾经解决过的音讯, 这就是 ”at least once”.
“Kafka Cluster” 到消费者的场景中能够采取以下计划来失去“恰好 1 次”的一致性语义:起码 1 次+消费者的输入中额定减少已解决音讯最大编号:因为已解决音讯最大编号的存在,不会呈现反复解决音讯的状况。 - 正本:kafka 中,replication 策略是基于 partition, 而不是 topic;kafka 将每个 partition 数据复制到多个 server 上, 任何一个 partition 有一个 leader 和多个 follower(能够没有); 备份的个数能够通过 broker 配置文件来设定。leader 解决所有的 read-write 申请,follower 须要和 leader 放弃同步.Follower 就像一个 ”consumer”, 生产音讯并保留在本地日志中;leader 负责跟踪所有的 follower 状态, 如果 follower” 落后 ” 太多或者生效,leader 将会把它从 replicas 同步列表中删除. 当所有的 follower 都将一条音讯保留胜利, 此音讯才被认为是 ”committed”, 那么此时 consumer 能力生产它, 这种同步策略, 就要求 follower 和 leader 之间必须具备良好的网络环境. 即便只有一个 replicas 实例存活, 依然能够保障音讯的失常发送和接管, 只有 zookeeper 集群存活即可.
抉择 follower 时须要兼顾一个问题, 就是新 leader server 上所曾经承载的 partition leader 的个数, 如果一个 server 上有过多的 partition leader, 意味着此 server 将接受着更多的 IO 压力. 在选举新 leader, 须要思考到 ” 负载平衡 ”,partition leader 较少的 broker 将会更有可能成为新的 leader. - 分布式:kafka 应用 zookeeper 来存储一些 meta 信息, 并应用了 zookeeper watch 机制来发现 meta 信息的变更并作出相应的动作 (比方 consumer 生效, 触发负载平衡等)。
Broker node registry: 当一个 kafka broker 启动后, 首先会向 zookeeper 注册本人的节点信息(长期 znode), 同时当 broker 和 zookeeper 断开连接时, 此 znode 也会被删除.
Broker Topic Registry: 当一个 broker 启动时, 会向 zookeeper 注册本人持有的 topic 和 partitions 信息, 依然是一个长期 znode.
Consumer and Consumer group: 每个 consumer 客户端被创立时, 会向 zookeeper 注册本人的信息; 此作用次要是为了 ” 负载平衡 ”. 一个 group 中的多个 consumer 能够交织的生产一个 topic 的所有 partitions; 简而言之, 保障此 topic 的所有 partitions 都能被此 group 所生产, 且生产时为了性能思考, 让 partition 绝对平衡的扩散到每个 consumer 上.
Consumer id Registry: 每个 consumer 都有一个惟一的 ID(host:uuid, 能够通过配置文件指定, 也能够由系统生成), 此 id 用来标记消费者信息.
Consumer offset Tracking: 用来跟踪每个 consumer 目前所生产的 partition 中最大的 offset. 此 znode 为长久节点, 能够看出 offset 跟 group_id 无关, 以表明当 group 中一个消费者生效, 其余 consumer 能够持续生产.
Partition Owner registry: 用来标记 partition 正在被哪个 consumer 生产. 长期 znode。此节点表白了 ” 一个 partition” 只能被 group 下一个 consumer 生产, 同时当 group 下某个 consumer 生效, 那么将会触发负载平衡(即: 让 partitions 在多个 consumer 间平衡生产, 接管那些 ” 游离 ” 的 partitions)。
当 consumer 启动时, 所触发的操作:
A) 首先进行 ”Consumer id Registry”;
B) 而后在 ”Consumer id Registry” 节点下注册一个 watch 用来监听以后 group 中其余 consumer 的 ”leave” 和 ”join”; 只有此 znode path 下节点列表变更, 都会触发此 group 下 consumer 的负载平衡.(比方一个 consumer 生效, 那么其余 consumer 接管 partitions).
C) 在 ”Broker id registry” 节点下, 注册一个 watch 用来监听 broker 的存活状况; 如果 broker 列表变更, 将会触发所有的 groups 下的 consumer 从新 balance.
总结:
1) Producer 端应用 zookeeper 用来 ” 发现 ”broker 列表, 以及和 Topic 下每个 partition leader 建设 socket 连贯并发送音讯.
2) Broker 端应用 zookeeper 用来注册 broker 信息, 曾经监测 partition leader 存活性.
3) Consumer 端应用 zookeeper 用来注册 consumer 信息, 其中包含 consumer 生产的 partition 列表等, 同时也用来发现 broker 列表, 并和 partition leader 建设 socket 连贯,并获取音讯。 - Leader 的抉择:Kafka 的外围是日志文件,日志文件在集群中的同步是分布式数据系统最根底的因素。
如果 leaders 永远不会 down 的话咱们就不须要 followers 了!一旦 leader down 掉了,须要在 followers 中抉择一个新的 leader. 然而 followers 自身有可能延时太久或者 crash,所以必须抉择高质量的 follower 作为 leader. 必须保障,一旦一个音讯被提交了,然而 leader down 掉了,新选出的 leader 必须能够提供这条音讯。大部分的分布式系统采纳了少数投票法令抉择新的 leader, 对于少数投票法令,就是依据所有正本节点的情况动静的抉择最适宜的作为 leader.Kafka 并不是应用这种办法。
Kafka 动静保护了一个同步状态的正本的汇合(a set of in-sync replicas),简称 ISR,在这个汇合中的节点都是和 leader 放弃高度一致的,任何一条音讯必须被这个汇合中的每个节点读取并追加到日志中了,才回告诉内部这个音讯曾经被提交了。因而这个汇合中的任何一个节点随时都能够被选为 leader.ISR 在 ZooKeeper 中保护。ISR 中有 f + 1 个节点,就能够容许在 f 个节点 down 掉的状况下不会失落音讯并失常提供服。ISR 的成员是动静的,如果一个节点被淘汰了,当它从新达到“同步中”的状态时,他能够重新加入 ISR. 这种 leader 的抉择形式是十分疾速的,适宜 kafka 的利用场景。
如果所有节点都 down 掉了怎么办?Kafka 对于数据不会失落的保障,是基于至多一个节点是存活的,一旦所有节点都 down 了,这个就不能保障了。
理论利用中,当所有的正本都 down 掉时,必须及时作出反应。能够有以下两种抉择:
1:期待 ISR 中的任何一个节点复原并负责 leader。
2:抉择所有节点中(不只是 ISR)第一个复原的节点作为 leader.
这是一个在可用性和连续性之间的衡量。如果期待 ISR 中的节点复原,一旦 ISR 中的节点起不起来或者数据都是了,那集群就永远复原不了了。如果期待 ISR 意外的节点复原,这个节点的数据就会被作为线上数据,有可能和实在的数据有所出入,因为有些数据它可能还没同步到。Kafka 目前抉择了第二种策略,在将来的版本中将使这个策略的抉择可配置,能够依据场景灵便的抉择。
这种困境不只 Kafka 会遇到,简直所有的分布式数据系统都会遇到。 - 正本治理:以上仅仅以一个 topic 一个分区为例子进行了探讨,但实际上一个 Kafka 将会治理成千上万的 topic 分区.Kafka 尽量的使所有分区平均的散布到集群所有的节点上而不是集中在某些节点上,另外主从关系也尽量平衡这样每个几点都会负责肯定比例的分区的 leader.
优化 leader 的抉择过程也是很重要的,它决定了零碎产生故障时的空窗期有多久。Kafka 抉择一个节点作为“controller”, 当发现有节点 down 掉的时候它负责在游泳分区的所有节点中抉择新的 leader, 这使得 Kafka 能够批量的高效的治理所有分区节点的主从关系。如果 controller down 掉了,活着的节点中的一个会备切换为新的 controller. - Leader 与正本同步:
对于某个分区来说,保留正分区的 ”broker” 为该分区的 ”leader”,保留备份分区的 ”broker” 为该分区的 ”follower”。备份分区会齐全复制正分区的音讯,包含音讯的编号等附加属性值。为了放弃正分区和备份分区的内容统一,Kafka 采取的计划是在保留备份分区的 ”broker” 上开启一个消费者过程进行生产,从而使得正分区的内容与备份分区的内容保持一致。个别状况下,一个分区有一个“正分区”和零到多个“备份分区”。能够配置“正分区 + 备份分区”的总数量,对于这个配置,不同主题能够有不同的配置值。留神,生产者,消费者只与保留正分区的 ”leader” 进行通信。
Kafka 容许 topic 的分区领有若干正本,这个数量是能够配置的,你能够为每个 topic 配置正本的数量。Kafka 会主动在每个正本上备份数据,所以当一个节点 down 掉时数据仍然是可用的。
Kafka 的正本性能不是必须的,你能够配置只有一个正本,这样其实就相当于只有一份数据。
创立正本的单位是 topic 的分区,每个分区都有一个 leader 和零或多个 followers. 所有的读写操作都由 leader 解决,个别分区的数量都比 broker 的数量多的多,各分区的 leader 平均的散布在 brokers 中。所有的 followers 都复制 leader 的日志,日志中的音讯和程序都和 leader 中的统一。followers 向一般的 consumer 那样从 leader 那里拉取音讯并保留在本人的日志文件中。
许多分布式的音讯零碎主动的解决失败的申请,它们对一个节点是否着(alive)”有着清晰的定义。Kafka 判断一个节点是否活着有两个条件: - 节点必须能够保护和 ZooKeeper 的连贯,Zookeeper 通过心跳机制查看每个节点的连贯。
- 如果节点是个 follower, 他必须能及时的同步 leader 的写操作,延时不能太久。
合乎以上条件的节点精确的说应该是“同步中的(in sync)”,而不是含糊的说是“活着的”或是“失败的”。Leader 会追踪所有“同步中”的节点,一旦一个 down 掉了,或是卡住了,或是延时太久,leader 就会把它移除。至于延时多久算是“太久”,是由参数 replica.lag.max.messages 决定的,怎么算是卡住了,怎是由参数 replica.lag.time.max.ms 决定的。
只有当音讯被所有的正本退出到日志中时,才算是“committed”,只有 committed 的音讯才会发送给 consumer,这样就不必放心一旦 leader down 掉了音讯会失落。Producer 也能够抉择是否期待音讯被提交的告诉,这个是由参数 acks 决定的。
Kafka 保障只有有一个“同步中”的节点,“committed”的音讯就不会失落。