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. 理解Python中的yield

    1.通常的for...in...循环中,in后面是一个数组,这个数组就是一个可迭代对象,类似的还有链表,字符串,文件.它可以是mylist = [1, 2, 3],也可以是mylist = [x*x ...

  2. pymongo 学习总结

    1.简介 MongoDB是一种强大.灵活.追求性能.易扩展的数据存储方式.是面向文档的数据库,不是关系型数据库,是NoSQL(not only SQL)的一种.所谓的面向文档,就是将原来关系型数据库中 ...

  3. 开机进入grub命令行之后。。。。

    最近由于经常整理自己电脑上的文件,难免都会遇到误删系统文件或者操作失误导致系统不能够正常进入的情况.这时就会出现grub错误的提示,只能输入命令才能进入系统.那么该输入什么命令呢?其实非常简单. gr ...

  4. BZOJ_2161_布娃娃_权值线段树

    BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...

  5. 从MVC和三层架构说到SSH整合开发

    相信很多人都认同JavaWeb开发是遵从MVC开发模式的,遵从三层架构进行开发的,是的,大家都这么认同.但是相信大家都会有过这样一个疑问,if(MVC三层模式==三层架构思想)out.println( ...

  6. C# 在异步线程操作类的变量

    如下代码: public partial class Form1 : Form { public Form1() { InitializeComponent(); } public string Me ...

  7. 从YOLOv1到v3的进化之路

    引言:如今基于深度学习的目标检测已经逐渐成为自动驾驶,视频监控,机械加工,智能机器人等领域的核心技术,而现存的大多数精度高的目标检测算法,速度较慢,无法适应工业界对于目标检测实时性的需求,这时YOLO ...

  8. c# 基于文件系统实现的队列处理类

    现实业务中经常遇到需要队列处理的问题. 问题场景: 客户端记录设备运行数据,传输给服务器.在传输中可能存在延迟或中断情况.当中断时,系统传输数据可能因为无法传输或电脑重启,会导致服务器数据记录不连续. ...

  9. 聊聊真实的 Android TV 开发技术栈

    智能电视越来越普及了,华为说四月发布智能电视跳票了,一加也说今后要布局智能电视,在智能电视方向,小米已经算是先驱了.但是还有不少开发把智能电视简单的理解成手机屏幕的放大,其实这两者并不一样. 一.序 ...

  10. nodeJs的nodemailer发邮件报错hostname/IP doesn't match certificate's altnames怎么解决?

    今天在开发过程中碰到一个问题,即使用node发送邮件时报错hostname/IP doesn't match certificate's altnames,在网上查了解决办法有两个, 加rejectU ...