首先Kafka是一个分布式消息队列中间件,Apache顶级项目,https://kafka.apache.org/   高性能、持久化、多副本备份、横向扩展。

生产者Producer往队列里发送消息,消费者Consumer从队列里消费消息,然后进行业务逻辑。应用场景主要有:解耦、削峰(缓冲)、异步处理、排队、分布式事务控制等等。

  1. Kafka对外使用Topic(主题)的概念,生产者往Topic里写消息,消费者从Topic中消费读消息。
  2. 为了实现水平扩展,一个Topic实际是由多个Partition(分区)组成的,遇到瓶颈时,可以通过增加Partition的数量来进行横向扩容。
  3. 单个Parition内是保证消息有序。持久化时,每收到一条消息,Kafka就是在对应的日志文件Append写,所以性能非常高。

Kafka Data Flow 消息流转图

上图中,消息生产者Producers往Brokers里面的指定Topic中写消息,消息消费者Consumers从Brokers里面消费指定Topic的消息,然后进行业务处理。

在实际的部署架构中,Broker、Topic、Partition这些元数据保存在ZooKeeper中,Kafka的监控、消息路由(分区)由ZooKeeper控制。0.8版本的OffSet也由ZooKeeper控制。

一、消息生产/发送过程

Kafka创建Message、发送时要指定对应的Topic和Value(消息体),Key(分区键)和Partition(分区)是可选参数。

调用Producer的Send()方法后,消息先进行序列化(消息序列化器可自定义实现:例如:Protobuf),然后按照Topic和Partition,临时放到内存中指定的发送队列中。达到阈值后,然后批量发送。

发送时,当Partition没设置时,如果设置了Key-分区键(例如:单据类型),按照Key进行Hash取模,保证相同的Key发送到指定的分区Partition。如果未设置分区键Key,使用Round-Robin轮询随机选分区Partition。

二、分区Partition的高可用和选举机制

分区有副本的概念,保证消息不丢失。当存在多副本的情况下,会尽量把多个副本,分配到不同的broker上。

Kafka会为Partition选出一个Leader Broker(通过ZooKeeper),之后所有该Partition的请求,实际操作的都是Leader,然后再同步到其他的Follower。

当一个Kafka Broker宕机后,所有Leader在该Broker上的Partition都会重新选举,在剩余的Follower中选出一个Leader,继续提供服务。

正如上面所讲:Kafka使用ZooKeeper在多个Broker中选出一个Controller,用于Partition分配和Leader选举。以下是Partition的分配机制:

  • 将所有Broker(假设共n个Broker)和待分配的Partition排序
  • 将第i个Partition分配到第(i mod n)个Broker上 (这个就是leader)
  • 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上

Controller会在ZooKeeper的/brokers/ids节点上注册Watch,一旦有broker宕机,它就能知道。

当Broker宕机后,Controller就会给受到影响的Partition选出新Leader。

Controller从ZooKeeper的/brokers/topics/[topic]/partitions/[partition]/state中,读取对应Partition的ISR(in-sync replica已同步的副本)列表,选一个出来做Leader。

选出Leader后,更新ZooKeeper的存储,然后发送LeaderAndISRRequest给受影响的Broker进行通知。

如果ISR列表是空,那么会根据配置,随便选一个replica做Leader,或者干脆这个partition就是宕机了。

如果ISR列表的有机器,但是也宕机了,那么还可以等ISR的机器活过来。

多副本同步:

服务端这边的处理是Follower从Leader批量拉取数据来同步。但是具体的可靠性,是由生产者来决定的。

生产者生产消息的时候,通过request.required.acks参数来设置数据的可靠性。

在acks=-1的时候,如果ISR少于min.insync.replicas指定的数目,那么就会返回不可用。

这里ISR列表中的机器是会变化的,根据配置replica.lag.time.max.ms,多久没同步,就会从ISR列表中剔除。以前还有根据落后多少条消息就踢出ISR,在1.0版本后就去掉了,因为这个值很难取,在高峰的时候很容易出现节点不断的进出ISR列表。

从ISA中选出leader后,follower会从把自己日志中上一个高水位后面的记录去掉,然后去和leader拿新的数据。因为新的leader选出来后,follower上面的数据,可能比新leader多,所以要截取。这里高水位的意思,对于partition和leader,就是所有ISR中都有的最新一条记录。消费者最多只能读到高水位;

从leader的角度来说高水位的更新会延迟一轮,例如写入了一条新消息,ISR中的broker都fetch到了,但是ISR中的broker只有在下一轮的fetch中才能告诉leader。

也正是由于这个高水位延迟一轮,在一些情况下,kafka会出现丢数据和主备数据不一致的情况,0.11开始,使用leader epoch来代替高水位。

三、消息消费过程

 
订阅topic是以一个消费组来订阅的,一个消费组里面可以有多个消费者。同一个消费组中的两个消费者,不会同时消费一个partition。
换句话来说,就是一个partition,只能被消费组里的一个消费者消费,但是可以同时被多个消费组消费。因此,如果消费组内的消费者如果比partition多的话,那么就会有个别消费者一直空闲。
 消息Offset偏移量(消息的顺序号)管理
 一个消费组消费partition,需要保存offset记录消费到哪,以前保存在zk中,由于zk的写性能不好,以前的解决方法都是consumer每隔一分钟上报一次。
 ZooKeeper的性能严重影响了消费的速度,而且很容易出现重复消费。
 在0.10版本后,Kafka把这个offset的保存,从ZooKeeper总剥离,保存在一个名叫__consumeroffsets topic的Topic中。
 消息的key由groupid、topic、partition组成,value是偏移量offset。topic配置的清理策略是compact。总是保留最新的key,其余删掉。
 一般情况下,每个key的offset都是缓存在内存中,查询的时候不用遍历partition,如果没有缓存,第一次就会遍历partition建立缓存,然后查询返回。
 
 Partitin的Rebalance
 生产过程中broker要分配partition,消费过程这里,也要分配partition给消费者。类似broker中选了一个controller出来,消费也要从broker中选一个coordinator,用于分配partition。
 
 coordinator的选举过程
  1. 看offset保存在那个partition
  2. 该partition leader所在的broker就是被选定的coordinator

交互流程

  1. consumer启动、或者coordinator宕机了,consumer会任意请求一个broker,发送ConsumerMetadataRequest请求,broker会按照上面说的方法,选出这个consumer对应coordinator的地址。
  2. consumer 发送heartbeat请求给coordinator,返回IllegalGeneration的话,就说明consumer的信息是旧的了,需要重新加入进来,进行reblance。返回成功,那么consumer就从上次分配的partition中继续执行。

  Rebalance

  1. consumer给coordinator发送JoinGroupRequest请求。
  2. 这时其他consumer发heartbeat请求过来时,coordinator会告诉他们,要reblance了。
  3. 其他consumer发送JoinGroupRequest请求。
  4. 所有记录在册的consumer都发了JoinGroupRequest请求之后,coordinator就会在这里consumer中随便选一个leader。然后回JoinGroupRespone,这会告诉consumer你是follower还是leader,对于leader,还会把follower的信息带给它,让它根据这些信息去分配partition
  5. consumer向coordinator发送SyncGroupRequest,其中leader的SyncGroupRequest会包含分配的情况。
  6. coordinator回包,把分配的情况告诉consumer,包括leader。

当partition或者消费者的数量发生变化时,都得进行reblance。
   列举一下会reblance的情况:

  1. 增加Partition
  2. 增加消费者
  3. 消费者主动关闭
  4. 消费者宕机
  5. coordinator宕机

四、消息投递语义

kafka支持3种消息投递语义,
At most once:最多一次,消息可能会丢失,但不会重复
At least once:最少一次,消息不会丢失,可能会重复
Exactly once:只且一次,消息不丢失不重复,只且消费一次(0.11中实现,仅限于下游也是kafka)

At least once:(业务中使用比较多)

先获取数据,再进行业务处理,业务处理成功后commit offset。

  1. 生产者生产消息异常,消息是否成功写入不确定,重做,可能写入重复的消息
  2. 消费者处理消息,业务处理成功后,更新offset失败,消费者重启的话,会重复消费

At most once:

先获取数据,再commit offset,最后进行业务处理。

  1. 生产者生产消息异常,不管,生产下一个消息,消息就丢了
  2. 消费者处理消息,先更新offset,再做业务处理,做业务处理失败,消费者重启,消息就丢了。

Exactly once:

首先要保证消息不丢,再去保证不重复。所以盯着At least once的原因来搞。

  1. 生产者重做导致重复写入消息----生产保证幂等性
  2. 消费者重复消费---消灭重复消费,或者业务接口保证幂等性重复消费也没问题

业务处理的幂等性非常重要。Kafka控制不了,需要业务来实现。比如所判断消息是否已经处理。

解决重复消费有两个方法:

  1. 下游系统保证幂等性,重复消费也不会导致多条记录。
  2. 把commit offset和业务处理绑定成一个事务。

生产的幂等性:

为每个producer分配一个pid,作为该producer的唯一标识。producer会为每一个<topic,partition>维护一个单调递增的seq。类似的,broker也会为每个<pid,topic,partition>记录下最新的seq。当req_seq == broker_seq+1时,broker才会接受该消息。因为:

  1. 消息的seq比broker的seq大超过时,说明中间有数据还没写入,即乱序了。
  2. 消息的seq不比broker的seq小,那么说明该消息已被保存。

  事务性和原子性

场景是这样的:

  1. 先从多个源topic中获取数据。
  2. 做业务处理,写到下游的多个目的topic。
  3. 更新多个源topic的offset。

其中第2、3点作为一个事务,要么全成功,要么全失败。这里得益与offset实际上是用特殊的topic去保存,这两点都归一为写多个topic的事务性处理。

引入tid(transaction id),和pid不同,这个id是应用程序提供的,用于标识事务,和producer是谁并没关系。就是任何producer都可以使用这个tid去做事务,这样进行到一半就死掉的事务,可以由另一个producer去恢复。
   同时为了记录事务的状态,类似对offset的处理,引入transaction coordinator用于记录transaction log。在集群中会有多个transaction coordinator,每个tid对应唯一一个transaction coordinator。
   注:transaction log删除策略是compact,已完成的事务会标记成null,compact后不保留。
   启动事务时,先标记开启事务,写入数据,全部成功就在transaction log中记录为prepare commit状态,否则写入prepare abort的状态。之后再去给每个相关的partition写入一条marker(commit或者abort)消息,标记这个事务的message可以被读取或已经废弃。成功后     在transaction log记录下commit/abort状态,至此事务结束。

整体的数据流是这样的:

  1. 首先使用tid请求任意一个broker(代码中写的是负载最小的broker),找到对应的transaction coordinator。
  2. 请求transaction coordinator获取到对应的pid,和pid对应的epoch,这个epoch用于防止僵死进程复活导致消息错乱,当消息的epoch比当前维护的epoch小时,拒绝掉。tid和pid有一一对应的关系,这样对于同一个tid会返回相同的pid。
  3. client先请求transaction coordinator记录<topic,partition>的事务状态,初始状态是BEGIN,如果是该事务中第一个到达的<topic,partition>,同时会对事务进行计时;client输出数据到相关的partition中;client再请求transaction coordinator记录offset的<topic,partition>事务状态;client发送offset commit到对应offset partition。
  4. client发送commit请求,transaction coordinator记录prepare commit/abort,然后发送marker给相关的partition。全部成功后,记录commit/abort的状态,最后这个记录不需要等待其他replica的ack,因为prepare不丢就能保证最终的正确性了。

这里prepare的状态主要是用于事务恢复,例如给相关的partition发送控制消息,没发完就宕机了,备机起来后,producer发送请求获取pid时,会把未完成的事务接着完成。

当partition中写入commit的marker后,相关的消息就可被读取。所以kafka事务在prepare commit到commit这个时间段内,消息是逐渐可见的,而不是同一时刻可见。

    消息消费事务

    消费时,partition中会存在一些消息处于未commit状态,即业务方应该看不到的消息,需要过滤这些消息不让业务看到,kafka选择在消费者进程中进行过来,而不是在broker中过滤,主要考虑的还是性能。kafka高性能的一个关键点是zero copy,如果需要在broker中过 滤,那么势必需要读取消息内容到内存,就会失去zero copy的特性。

  五、 Kafka文件组织

  kafka的数据,实际上是以文件的形式存储在文件系统的。topic下有partition,partition下有segment,segment是实际的一个个文件,topic和partition都是抽象概念。

在目录/${topicName}-{$partitionid}/下,存储着实际的log文件(即segment),还有对应的索引文件。

每个segment文件大小相等,文件名以这个segment中最小的offset命名,文件扩展名是.log;segment对应的索引的文件名字一样,扩展名是.index。有两个index文件,一个是offset index用于按offset去查message,一个是time index用于按照时间去查,其实这里可以优化合到一起,下面只说offset index。总体的组织是这样的:

为了减少索引文件的大小,降低空间使用,方便直接加载进内存中,这里的索引使用稀疏矩阵,不会每一个message都记录下具体位置,而是每隔一定的字节数,再建立一条索引。 索引包含两部分,分别是baseOffset,还有position。

baseOffset:意思是这条索引对应segment文件中的第几条message。这样做方便使用数值压缩算法来节省空间。例如kafka使用的是varint。

position:在segment中的绝对位置。

查找offset对应的记录时,会先用二分法,找出对应的offset在哪个segment中,然后使用索引,在定位出offset在segment中的大概位置,再遍历查找message。

六、Kafka常用配置项
  Broker配置

  
  Topic配置
  
 
  参考链接:123archu 链接:https://www.jianshu.com/p/d3e963ff8b70 来源:简书

 

Kafka基本知识整理的更多相关文章

  1. kafka知识整理

    title: kafka知识整理 date: 2019-06-18 10:59:46 categories: MQ tags: kafka --- 转载自:https://www.cnblogs.co ...

  2. js事件(Event)知识整理

    事件(Event)知识整理,本文由网上资料整理而来,需要的朋友可以参考下   鼠标事件 鼠标移动到目标元素上的那一刻,首先触发mouseover 之后如果光标继续在元素上移动,则不断触发mousemo ...

  3. Kali Linux渗透基础知识整理(四):维持访问

    Kali Linux渗透基础知识整理系列文章回顾 维持访问 在获得了目标系统的访问权之后,攻击者需要进一步维持这一访问权限.使用木马程序.后门程序和rootkit来达到这一目的.维持访问是一种艺术形式 ...

  4. Kali Linux渗透基础知识整理(二)漏洞扫描

    Kali Linux渗透基础知识整理系列文章回顾 漏洞扫描 网络流量 Nmap Hping3 Nessus whatweb DirBuster joomscan WPScan 网络流量 网络流量就是网 ...

  5. wifi基础知识整理

    转自 :http://blog.chinaunix.net/uid-9525959-id-3326047.html WIFI基本知识整理 这里对wifi的802.11协议中比较常见的知识做一个基本的总 ...

  6. 数据库知识整理<一>

    关系型数据库知识整理: 一,关系型数据库管理系统简介: 1.1使用数据库的原因: 降低存储数据的冗余度 提高数据的一致性 可以建立数据库所遵循的标准 储存数据可以共享 便于维护数据的完整性 能够实现数 ...

  7. 【转载】UML类图知识整理

    原文:UML类图知识整理 UML类图 UML,进阶必备专业技能,看不懂UML就会看不懂那些优秀的资料. 这里简单整理 类之间的关系 泛化关系(generalization) 泛化(generalize ...

  8. Linux进程管理知识整理

    Linux进程管理知识整理 1.进程有哪些状态?什么是进程的可中断等待状态?进程退出后为什么要等待调度器删除其task_struct结构?进程的退出状态有哪些? TASK_RUNNING(可运行状态) ...

  9. js事件(Event)知识整理[转]

    事件注册 平常我们绑定事件的时候用dom.onxxxx=function(){}的形式 这种方式是给元素的onxxxx属性赋值,只能绑定有一个处理句柄. 但很多时候我们需要绑定多个处理句柄到一个事件上 ...

随机推荐

  1. IPFS学习-IPNS

    星际名称系统(IPNS)是一个创建个更新可变的链接到IPFS内容的系统,由于对象在IPFS中是内容寻址的,他们的内容变化将导致地址随之变化.对于多变的事物是有用的.但是很难获取某些内容的最新版本. 在 ...

  2. 如何给div、p添加onload事件?

    前提 其实只有 <body>.<frame>.<iframe>.<img>.<link>.<script>.<style& ...

  3. centos7配置阿里yum源

    首先刚刚安装完的centos并不像Ubuntu系统那样有很多的源来可以供您使用 所以我们需要通过下载阿里云的yum源在下载epel-release.noarch扩展包就可以了 操作如下: 1.首先这是 ...

  4. 形如 T(n) = a * T(n/b) + f(n) 的时间复杂度计算方法

    形如 T(n) = a * T(n/b) + f(n) 的时间复杂度计算方法 有一种方法叫做主方法(Master method)是用来专门计算这种形式的时间复杂度的,方法具体如下: 下边举例进行说明: ...

  5. .NET机器学习 ML.NET 1.4预览版和模型生成器更新

    ML.NET 是面向.NET开发人员的开源和跨平台机器学习框架. ML.NET  还包括Model Builder  (一个简单的UI工具)和  CLI  ,使用自动机器学习(AutoML)构建自定义 ...

  6. ubuntu下安装截图工具

    安装shutter 1.添加安装包软件源 sudo add-apt-repository ppa:shutter/ppa 2.更新软件源并且安装 sudo apt-get update sudo ap ...

  7. webpack 使用style-loader,css-loader添加css样式

    // 注意:webpack 默认只能打包处理js类型的文件// 如果要处理非js类型文件,需要手动安装第三方加载器// 1安装npm install style-loader css-loader - ...

  8. Linux下使用docker 拉取 vsftpd 镜像搭建 Ftp 服务器,连接 Ftp 时遇到的错误(425 Failed to establish connection)

    Ftp踩坑系列: Linux上的ftp服务器 vsftpd 之配置满天飞--设置匿名用户访问(不弹出用户名密码框)以及其他用户可正常上传 ftp服务器Serv-U 设置允许自动创建不存在的目录 FTP ...

  9. C# -- LinkedList的使用

    C# -- LinkedList的使用 private static void TestLinkList() { LinkedList<Person> linkListPerson = n ...

  10. ES中index和type区分

    参考: https://bayescafe.com/database/elasticsearch-using-index-or-type.html https://www.cnblogs.com/hu ...