1.动机

设计 kafka 初衷,作为统一平台处理大公司的实时数据。所以 必须具有如下特性:

  • 支持海量数据
  • 高吞吐量
  • 低延迟(实时性)
  • 支持分区,分布式
  • 容错

2.持久化

kafka 高度依赖 文件系统 存储和缓存消息。通过对磁盘的顺序读写,并借助 OS 层面的 页缓存(page cache),保证优于缓存在内存中或其他结构中。

为何使用磁盘效率仍然很高:

  • 利用磁盘的顺序读写,操作一个文件,将数据追加到文件的末尾。相比于随机读写,效率很高。
  • 利用 OS 层面的页缓存(page cache),顺序读文件可以预读数据到 page cache。通过自动访问所有可用内存 以及 存储紧凑型字节结构而非单个对象提高内存使用率。OS缓存相对于进程内的缓存,重启后仍然可用,不需要重建。
  • 所有的操作时间复杂度都是 常量时间O(1),与数据大小无关,读 和 写 不会互相阻塞。

3.效率

使用磁盘效率低下主要有两个原因:

  • 过多的小 I/O 操作:发生在客户端和服务端之间,以及 服务端自己的持久化操作中
  • 过多的字节复制

针对 小 I/O 操作,kafka 根据 "message set" 抽象构建了一个协议,该 抽象 自然地将消息分组在一起。该协议允许网络请求将消息分组在一起,并分摊网络往返的开销,而不是一次发送一条消息。服务器依次将消息块一次附加到其日志中,而消费者一次获取大型线性块。

针对过多的字节复制,使用了由生产者、代理 和 消费者共享的标准化二进制消息格式(这样,数据块就可以在它们之间不进行修改的情况下进行传输)。服务器所持有的消息日志 本身是一个文件目录,每个文件都由一系列 "message set" 填充。这些消息集以生产者和消费者使用的相同格式写入磁盘。维护这种通用格式可以优化  持久化日志块的 网络传输。

存储在文件中的信息通过网络发送给客户,经历的几个路径:

  • 操作系统在内核空间将数据从磁盘读取到 page cache 中。
  • 应用程序从内核空间读取到 用户空间缓冲区。
  • 应用程序将数据写回到内核空间的套接字缓冲区。
  • 操作系统将数据从套接字缓冲区复制到 NIC 缓冲区(NIC:网络接口控制器)。

以上产生了四个副本拷贝,2个系统调用开销,效率低下。

          

基于 零拷贝技术:消息数据直接从 page cache 发送到网络。linux 中使用 sendfile 完成零拷贝技术。java 中 java.nio.channels.FileChannel 的 transferTo() 方法也使用了零拷贝技术。

                  

kafka 通过 page cache 和 sendfile 的组合,将看不到磁盘上的任何读取活动,因为它们将完全从缓存中提供数据。

端到端的批量压缩

Kafka通过递归消息集来支持同时压缩多个消息而减少相同消息的冗余。 一批消息可以一起压缩并以此形式发送到服务器。 这批消息将以压缩形式写入,并将在日志中保持压缩,并且只能由消费者解压缩。Kafka支持GZIP和Snappy压缩协议。

4.生产者

4.1负载均衡

生产者将数据直接发送给分区对应的 leader。为了实现这一点,所有的 kafka 节点要能够在 任何时候应答 哪个服务器还活着以及 topic分区的leader在哪里的 元数据请求。

客户端自己控制 消息发送到哪个分区,这可以随机完成,实现一种随机的负载平衡,也可以通过一些语义分区函数完成。

4.2异步发送

启用 kafka 生产者 的批处理,kafka 将在内存中累积数据然后一次性批量发送。可以配置 累计不超过固定数量的消息(bach.size),等待不超过固定延迟时间(linger.ms)。

5.消费者

5.1拉 VS 推送

消费者主动拉取消息缺点:如果 broker 没有数据,消费者会轮询,忙等待直到数据到达。kafka 可以在拉请求中设置一些参数,允许使用者请求在“长轮询”中阻塞,等待数据到达(也可以选择等待,直到给定的字节数可用,以确保传输大小很大)

消费者被动推送消息缺点:很难适应消费速率不同的消费者,消息发送速率是由 broker 决定的,broker 是尽可能快的将消息发送出去,这样会造成消费者来不及处理消息,典型的表现就是 网络阻塞 和 拒绝服务。

5.2消费者的定位

topic 被分为一组有序的分区,每个分区在任何给定的时间都由每个订阅消费者组中的一个消费者消费。这意味着消费者在每个分区中的位置只是一个整数,这个整数代表了即将要消费的消息的偏移量。这样做的好处是可以返回到旧的偏移量进行消费。

5.3离线数据加载

可伸缩持久性允许消费者只定期使用,例如批量数据加载,定期将数据批量加载到离线系统(如Hadoop或关系数据仓库)中。

6.消息传递语义

很明显,消息传递保证能够提供多种可能:

  • 最多一次:消息可能丢失,但是绝不会重发
  • 至少一次:消息绝不会丢失,但是可能会重发
  • 正好一次:每条消息被传递一次

kafka 的消息传递语义:

一旦发布的消息已提交到日志,只要副本分区写入了此消息的一个broker仍然"活着”,它就不会丢失。

0.11.0.0 版本之前,如果一个生产者没有收到消息提交的响应,那么生产者只能重新发送该消息。这就保证了至少一次的传递语义。如果上一次的请求实际上是成功的,那么消息就会再次写到日志中,造成重复消费。

0.11.0.0 版本之后,kafka 生产者支持幂等传递,保证重新发送不会导致日志中有重复记录。为了实现这一点,broker 为 每一个生产者 分配一个 ID,使用生产者随每条消息一起发送的序列号来消除重复的消息。

同时也是从 0.11.0.0 版本之后,生产者支持使用事务类语义将消息发送到多个 topic 分区的能力:即,要么所有消息都已成功写入,要么都未成功写入。这方面的主要用例是在Kafka topic 之间进行一次处理。

当然,不是所有的使用场景都需要如此严谨的保障,对于延迟敏感的,我们允许生产者指定它想要的耐用性水平。如生产者可以指定它获取需等待10毫秒量级上的响应。生产者也可以指定异步发送,或只等待leader(不需要副本的响应)有响应。

从消费者的角度描述语义:

  • 读取到消息,在日志中保存位置,最后处理消息。这种顺序 如果消费者在保存位置之后,处理消息之前崩溃,数据会丢失,属于 最多一次的语义。
  • 读取消息,处理消息,在日志中保存位置。这种顺序,如果消费者在处理消息之后,日志中保存位置之前崩溃,数据会被多次处理,属于至少一次的语义。在多数情况下,消息都有一个主键,所以更新是幂等的(一次执行和多次执行的影响相同)。

kafka 默认是保证“至少一次”传递,并允许用户通过禁止生产者重试和处理一批消息前提交它的偏移量来实现 “最多一次”传递。而“正好一次”传递需要与目标存储系统合作,但kafka提供了偏移量,所以实现这个很简单。

7.副本

kafka 在各个服务器上备份 每个 topic 的 partition (通过 replication factor 设置副本数)。当集群中的某个服务器发生故障时,自动转移到这些副本,以便在故障时,消息仍然可用。

kafka 的默认 副本因子为 1,即不创建副本。副本因子是指副本的总数,包括 leader 。

副本以 topic 的 partition 为单位。在非故障的情况下,kafka 中的每个 partition 都有一个 leader,0 个或者多个 follower。所有的读 和写都指向 分区的 leader。通常,分区数 多于 broker 的数量,leader 均匀的分布在 broker 上。follower 的日志与 leader 的日志相同,即相同的 偏移量 offset 和 消息顺序 。(当然,有可能在某个时间点,leader 上比 follower 多几条还未同步的消息)。

kafka 节点存活的2个条件:

  • 一个节点必须能维持与 zookeeper 的会话(通过 zookeeper 的心跳机制)。
  • 如果该节点是  slave,它必须复制 leader 的写数据,并且不能落后太多。

如果节点 死掉,卡主,或者落后太多,leader 将 从 同步副本 ISR (In Sync Replicas)中移除该节点。落后多少是由  replica.lag.max.messages 控制,卡主多久算卡主是由  replica.lag.time.max.ms 控制。

kafka 动态维护一组同步 leader 数据的副本(ISR),只有这个组中的成员才有资格当选 leader。在所有同步副本都收到写操作之前,不会认为已提交对Kafka分区的写操作。这组 ISR 保存在 zookeeper 中,正因为如此,在ISR中的任何副本都有资格当选leader。对于 f+1 个 副本的 kafka, topic 可以容忍f失败而不会丢失已提交的消息。

如果所有的节点都死掉,有两种可以实现的方式:

  • 等待 ISR 列表中的节点活过来,并且选择该节点作为 leader.
  • 选择第一个活过来的节点(不管它在 ISR 列表中)作为 leader.

从 0.11.0.0 开始 kafka 默认选择第一种策略,等待一致性的副本;可以通过配置 unclean.leader.election.enable 为 true 来选用第二种策略。这两种策略是  可用性 和一致性的权衡,需要根据实际业务来决定。

可用性 和 耐久性保证

当写消息到 kafka 时,生产者可以 配置 需要 leader 收到的确认数 来确定是否完成请求,通过 配置 acks 满足多种情况:

  • acks = 0 :生产者不会等待服务器的任何确认,消息记录将被立刻添加到  socket 缓冲区并视为已发送。这种情况无法确保服务器已经接收到消息记录,重试的配置也不会生效。每个记录返回的偏移量始终被设置为 1.
  • acks = 1 :服务器端的 leader 写入消息到本地日志就立即响应生产者,而不等待 follower 应答。这种情况,如果在服务器响应生产者之后,复制到 follower 之前挂掉 就会丢失数据。
  • acks = all(-1):服务器端的 leader 会等待 ISR 中所有副本同步响应来确认消息记录。这保证了只要 ISR 中还有一个副本存活就不会丢失记录,也可以设置为 -1;

提供两种 topic 级别的配置 来确保 持久性 而非 可用性。

  • unclean.leader.election.enable 设为 false,(默认即为 false)即 所有的副本都不可用时,分区才不可用。只有当 ISR 中的节点 活过来 分区才能可用。
  • 指定 一个最小的 ISR 数量值,通过 min.insync.replicas 来配置,只有当 ISR 中的数量 超过最小值,分区才会接受写入操作,以此来防止仅写入单个副本而后副本不可用而导致的消息的丢失。该设置仅在 acks = all 并保证至少有这么多同步副本确认消息时生效。

副本管理

上面关于复制日志的讨论实际上只涉及了一个日志,例如 一个 topic 的partition,然而,kafka 集群管理着成百上千个这样的分区。通过 round-robin 的方式平衡 集群中的分区,避免 大部分的分区分布在少量的及诶单上,同样,平衡 leader,使在分区份额上的每个节点都是 leader。

kafka 选择 其中一个 broker 作为 controller(到 zookeeper 上注册,先到先得)。该 controller 检测 broker 级别的故障,并负责更改 故障 broker 上受影响的 分区的 leader。这样就可以批量处理 leader 的变更。如果 controller 故障,其他存活的 broker 将会成为新的 controller(同样需要到 zookeeper 上注册)。

kafka的设计的更多相关文章

  1. Kafka/Metaq设计思想学习笔记 转

    转载自: http://my.oschina.net/geecoodeer/blog/194829 本文没有特意区分它们之间的区别,仅仅是列出其中笔者认为好的设计思想,供后续设计参考. 目前笔者并没有 ...

  2. 分布式发布订阅消息系统 Kafka 架构设计

    我们为什么要搭建该系统 Kafka是一个分布式.分区的.多副本的.多订阅者的“提交”日志系统. 我们构建这个系统是因为我们认为,一个实现完好的操作日志系统是一个最基本的基础设施,它可以替代一些系统来作 ...

  3. 分布式发布订阅消息系统 Kafka 架构设计[转]

    分布式发布订阅消息系统 Kafka 架构设计 转自:http://www.oschina.net/translate/kafka-design 我们为什么要搭建该系统 Kafka是一个消息系统,原本开 ...

  4. Kafka设计解析(四)Kafka Consumer设计解析

    转载自 技术世界,原文链接 Kafka设计解析(四)- Kafka Consumer设计解析 目录 一.High Level Consumer 1. Consumer Group 2. High Le ...

  5. Kafka详解四:Kafka的设计思想、理念

    问题导读 1.Kafka的设计基本思想是什么?2.Kafka消息转运过程中是如何确保消息的可靠性的? 本节主要从整体角度介绍Kafka的设计思想,其中的每个理念都可以深入研究,以后我可能会发专题文章做 ...

  6. 分布式公布订阅消息系统 Kafka 架构设计

    我们为什么要搭建该系统 Kafka是一个消息系统,原本开发自LinkedIn,用作LinkedIn的活动流(activity stream)和运营数据处理管道(pipeline)的基础. 如今它已为多 ...

  7. 流式计算新贵Kafka Stream设计详解--转

    原文地址:https://mp.weixin.qq.com/s?__biz=MzA5NzkxMzg1Nw==&mid=2653162822&idx=1&sn=8c4611436 ...

  8. kafka具体解释四:Kafka的设计思想、理念

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/suifeng3051/article/details/37606001      本节主要从总体角度 ...

  9. 深入理解Kafka核心设计及原理(三):消费者

    转载请注明出处:https://www.cnblogs.com/zjdxr-up/p/16114877.html 深入理解Kafka核心设计及原理(一):初识Kafka 深入理解Kafka核心设计及原 ...

  10. 深入理解Kafka核心设计及原理(四):主题管理

    转载请注明出处:https://www.cnblogs.com/zjdxr-up/p/16124354.html 目录: 4.1创建主题 4.2 优先副本的选举 4.3 分区重分配 4.4 如何选择合 ...

随机推荐

  1. 软件配置管理及SVN的使用

    一.配置管理   1. 管理整个软件生命周期中的配置项    配置项:软件生命周期中产出的各种输出成果,如需求文档.设计文档.代码.测试相关文档   2.管理配置项的变化(核心)   3.使用配置管理 ...

  2. 写给小前端er的nodejs,mongodb后端小攻略~ (windows系统~)

    一.写在前面 迫于学校的压力,研二上准备回学校做实验发论文了,感觉真的没意思,这几天学着搞搞后端,踩了很多坑,整理一下这几天的坑以免以后再犯! 二.本文主要内容(由于是面向前端同学的,所以前端的内容就 ...

  3. javascript 中的apply()和call()方法详解

    1.语法 先来看看JS手册中对call的解释: call 方法 调用一个对象的一个方法,以另一个对象替换当前对象. call([thisObj[,arg1[, arg2[, [,.argN]]]]]) ...

  4. PHP与XML技术

    XML的概述 XML(eXtensibleMarkup Language),扩展性标记语言,它是用来描述其他语言的语言.它允许用户设计自己的标记.XML是由W3C(WorldWide 月发布的一种标准 ...

  5. 在Windows Server 2008 R2下搭建jsp环境(二)-JDK的下载安装

    因为服务器上的Tomcat的运行环境需要JDK的支持,所以,掌握JDK的安装与下载和配置是一个重要步骤.   1.首先下载最新的JDK版本.网络上提供了最新版本的JDK下载,如图所示.首先选择&quo ...

  6. Java - Instrumentation

    使用JRebel启动工程时加上VM参数时有一个参数是"-javaagent:D:\jrebel_5.6.0\jrebel.jar". javaagent是什么? java -hel ...

  7. 高数量类别特征(high-cardinality categorical attributes)的预处理方法

    high-cardinality categorical attributes,从字面上理解,即对于某个category特征,不同值的数量非常多,这里暂且把它叫做高数量类别属性.反之,即低数量类别属性 ...

  8. Jmeter-----【mac电脑】配置web浏览器的代理抓取请求

    在测试中,不仅会涉及到APP中的数据测试,时常我们APP的数据需要与后台进行交互,因此我们不可避免的也需要对web进行接口测试,更准确的来说是使用web的接口来快速的帮我们实现App中所需的数据录入, ...

  9. Keras框架简介

    Keras是基于Theano的一个深度学习框架,它的设计参考了Torch,用Python语言编写,是一个高度模块化的神经网络库,支持GPU和CPU.使用文档在这:http://keras.io/,中文 ...

  10. Java核心技术梳理-基础类库

    一.引言 Oracle为Java提供了丰富的基础类库,Java 8 提供了4000多个基础类库,熟练掌握这些基础类库可以提高我们的开发效率,当然,记住所有的API是不可能也没必要的,我们可以通过API ...