kafka详解

kafka角色构成

  1. Producer :消息生产者,向kafka broker发消息的客户端;
  2. Consumer :消息消费者,从kafka broker取消息的客户端;
  3. Topic :数据存储在Topic中,可以理解为一个队列;
  4. Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic;
  5. Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
  6. Offset:partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序;
  7. Segment:partition物理上由多个segment组成,每个Segment存着message信息
  8. Leader:负责给定Partition的所有读取和写入的节点。
  9. Follower:跟随Leader指令的节点被称为Follower。 如果Leader节点宕机,其中一个Follower将通过选举自动成为新的Leader。
  10. Consumer Group (CG):这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。一个topic可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion只会把消息发给该CG中的一个consumer。如果需要实现广播,只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic;
  11. Zookeeper:为集群分布式一致性提供服务,以及在早期kafka版本保存消息相关元数据。

kafka的Partition机制(高可用)

Partition机制是kafka的文件存储结构,kafka的高可用就是依赖于这样的文件存储结构。

kafka中的Topic是由多个Partition组成的,Topic是逻辑上的概念,Partition是物理存储上的概念。

分区(Partition)

消息发送时都被发送到一个topic,其本质就是一个目录,而topic是由一些Partition Logs(分区日志)组成,其组织结构如下图所示:
在这里插入图片描述
在这里插入图片描述
kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka。

分区的原因

  1. 负载均衡,方便在集群中扩展。每个Partition可以通过调整以适应它所在的机器,而一个Topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了。
  2. 可以提高并发,因为可以以Partition为单位读写了。
Partition的物理存储与读取

每个partiton在物理上对应一个目录(文件夹),以topic名称+有序序号的形式命名(序号从0开始计,最大为partition数-1)。
在这里插入图片描述
每个分区文件夹下存储这个分区的所有消息(.log)和索引文件(.index)。“.index”索引文件存储大量的元数据,“.log”数据文件存储大量的消息,索引文件中的元数据指向对应数据文件中message的物理偏移地址。一个消息(.log)文件和它的索引文件(.index)是一个Segment。
如下图:
在这里插入图片描述
其中以“.index”索引文件中的元数据[3, 348]为例,在“.log”数据文件表示第3个消息,即在全局partition中表示170410+3=170413个消息,该消息的物理偏移地址为348。

通过offset查找message

那么如何从partition中通过offset查找message呢?大致分为三步:

  1. 找到相应的index文件
  2. 二分查找,找到index文件中对应的位置,得到消息所在的具体位置。
  3. 得到消息。

以上图为例,读取offset=170418的消息,首先查找segment文件,其中 00000000000000000000.index为最开始的文件,第二个文件为00000000000000170410.index(起始偏移为170410+1=170411),而第三个文件为00000000000000239430.index(起始偏移为239430+1=239431),所以这个offset=170418就落到了第二个文件之中。

其他 后续文件可以依次类推,以其实偏移量命名并排列这些文件,然后根据二分查找法就可以快速定位到具体文件位置。其次根据 00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置进行读取。

Kafka中topic的每个partition有一个预写式的日志文件,虽然partition可以继续细分为若干个segment文件,但是对于上层应用来说可以将 partition看成最小的存储单元(一个有多个segment文件拼接的“巨型”文件),每个partition都由一些列有序的、不可变的消息组成,这些消息被连续的追加到partition中。

消息队列是以log文件的形式存储,消息生产者只能将消息添加到既有的文件尾部,没有任何ID信息用于消息的定位,完全依靠文件内的位移,因此消息的使用者只能依靠文件位移顺序读取消息,这样也就不需要维护复杂的支持随即读取的索引结构。

那如何保证消息是按顺序消费的?

因为kafka可以将一个topic分为多个Partition,因此如果消费者同时取多个Partition的消息时,是无法保证消息的有序性的,但如果每个消费者只拿一个Partition中的消息,那这种消费消息就是有序的。

也就是单个Partition可以保证有序,多个Partition就无法保证。

那如何保证消息均匀的分布到不同的partition中?

选择分区的方式

  1. 已指定Partition,则直接使用该Partition。
  2. 未指定Partition但指定了Key,则通过对Key进行哈希计算得出一个Partition。
  3. Partition和Key都未指定,则轮询选出一个Partition。

生产者在生产数据的时候,可以为每条消息指定Key,这样消息被发送到broker时,会根据分区规则选择被存储到哪一个分区中,如果分区规则设置的合理,那么所有的消息将会被均匀的分布到不同的分区中,这样就实现了负载均衡和水平扩展。分区规则可以自定义,比如将消息的key做了hashcode,然后和分区数(numPartitions)做模运算,使得每一个key都可以分布到一个分区中。

链接:https://www.jianshu.com/p/f8f1d74a75cd

kafka的replication与leader机制(可靠性)

为什么有replication与leader机制

为了保证kafka的可靠性,Kafka每个topic的partition有N个副本。

假如只有一个partition,一旦Broker宕机,其上所有Partition的数据都不可被Consumer消费,同时Producer也不能再将数据存于其上的Partition。

replication机制

为了提高消息的可靠性,Kafka每个topic的partition有N个副本(replication),其中N(大于等于1)是topic的复制因子(replication fator)的个数。
这个时候每个 partition下面就有可能有多个 replication副本,但是这多个replication并不一定分布在一个broker上,有可能存储在其他的broker上,而这时候为了更好的在replication之间复制数据,此时会选出一个leader(leader机制)。

leader机制

而这时候为了更好的在replication之间复制数据,此时会选出一个Leader,Producer和Consumer只与这个Leader交互,其它Replication作为Follower从Leader中复制数据 。
producer会push消息到这个leader,consumer也会从这个leader pull 消息,其他的 replication 只是作为 follower 从leader复制数据,leader负责所有的读写,这样数据的一致性和有序性可以得到保证。

如何选出一个leader

当Leader宕机了,怎样在Follower中选举出新的Leader?
Kafka在Zookeeper中动态维护了一个ISR(in-sync replicas),这个ISR里的所有Replica都跟上了leader,只有ISR里的成员才有被选为Leader的可能。

选举最简单最直观的方案是:谁写进去谁就是leader

所有Follower都在Zookeeper上设置一个Watch,一旦Leader宕机,其对应的ephemeral znode会自动删除,此时所有Follower都尝试创建该节点,而创建成功者(Zookeeper保证只有一个能创建成功)即是新的Leader,其它Replica即为Follower。

链接:https://www.jianshu.com/p/f8f1d74a75cd

Producer写入消息(ACK机制)

  1. Producer发送消息
  2. 找到相应的Topic
  3. 找到Topic下的某个的Partition(方式上面有介绍)
  4. 找到Partition的Leader
  5. 向Leader写入数据,包括index和log文件
  6. Follower从Leader中pull消息
  7. Follower写入成功后返回ack,表明写入成功
  8. Leader向Producer返回ACK结果,表明写入成功。
    在这里插入图片描述

异步返回ack

为了提高性能,每个follower在接受到消息之后就会直接返回给leader ack消息,而并非等数据写入到log里(异步),所以,可以认为对于已经commit的数据,只可以保证消息已经存在与所有的replica的内存中,但是不保证已经被持久化到磁盘中。

producer确认写入成功

producer在写入消息后,需要确认是否真正的写入,但是它有三种级别可以选择,向 Kafka 写数据时,producer 设置 ack 是否提交完成:

  • 0:不等待leader replica的返回确认消息
  • 1:Leader 保存成功返回
  • -1:所有备份都保存成功返回

这三种机制,性能依次递减 (producer吞吐量降低1-3倍),数据健壮性则依次递增。

Consumer读取消息

消费的步骤:

  1. Consumer读取消息
  2. 找到对应的Topic
  3. 找到对应的partition
  4. 根据offset查找index文件
  5. 根据index文件查找log文件
  6. 读取消息成功

Consumer采用Pull模式从Broker中读取数据。

为什么选择pull方式

Push模式很难适应消费速率不同的Consumer,因为消息发送速率是由Broker决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成Consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而Pull模式则可以根据Consumer的消费能力以适当的速率消费消息。

对于Kafka而言,Pull模式更合适,它可简化Broker的设计,Consumer可自主控制消费消息的速率,同时Consumer可以自己控制消费方式——即可以批量消费也可以逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。

Pull模式不足之处是,如果Kafka没有数据,消费者可能会陷入循环中,一直等待数据到达。为了避免这种情况,可以Pull请求中设置参数,允许Consumer请求在等待数据到达的“长轮询”中进行阻塞(并且可选地等待到给定的字节数,以确保大的传输大小)。

Consumer Group

因为有多个Partition,Producer有三种方式选择写入哪个Partition,Consumer也需要有自己的方式来读取Partition,这就是Consumer Group。

Consumer是以Consumer Group的方式工作,由一个或者多个Consumer组成一个Group,共同消费一个Topic每个Partition在同一时间只能由Group中的一个Consumer读取,但是一个Consumer可以读取多个Partition,多个Group可以同时消费同一个Partition。某个Consumer读取某个Partition,也可以叫做某个Consumer是某个Partition的拥有者。

在这种情况下,Consumer可以通过水平扩展的方式同时读取大量的消息。另外,如果一个Consumer失败了,那么Group中其它的Consumer会自动负载均衡读取之前失败的Consumer负责的Partition。

在这里插入图片描述

链接:https://www.jianshu.com/p/f7725749e90d

Kafka的持久特性

Kafka的内存管理模式十分激进,基本的意思就是不管理。。。kafka不在JVM进程内部维护消息Cache,消息直接从文件中读写,使用文件存储消息,完全依赖操作系统在文件系统层面的cache,这就直接决定kafka在性能上严重依赖文件系统的本身特性

这样避免在JVM中管理Cache带来的额外数据结构开销和GC带来的性能代价。

kafka优化的方式主要有两种:

  1. 顺序读写
  2. 批量处理

无论任何 OS 下,对文件系统本身的优化几乎没有可能,kafka 是对日志文件进行 append 操作,顺序读写。因此磁盘检索的开支是较小的。
同时为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数,最大化利用文件系统的Cache机制和规避文件读写相对内存读写的性能代价。

链接:https://www.jianshu.com/p/f8f1d74a75cd

Kafka集群管理

Kafka也是一种服务,它也是由Zookeeper进行管理的。如下:
#### Producer生产过程
kafka的存活条件包括两个条件:

  1. 节点必须可以维护和zookeeper的连接,zookeeper通过心跳机制检查每个节点的连接(通过zookeeper的心跳机制)
  2. 如果节点是个follower,它必须能及时的同步leader的写操作,延时不能太久。

leader会跟踪与其保持着同步的replica列表简称ISR,(in-sync replica),如果一个follower宕机或是落后太多,leader就会把它从ISR中移除掉。

这里指的落后太多是说 follower复制的消息落后的超过了预设值,(该值可在KAFKA_HOME/config/server.properties中通过replica.lag.time.max.ms来配置,其默认值是10000)没有向leader发起fetch请求。

Kafka为什么会出现重复消费的情况

因为kafka的三个特性:

  1. kafka消费后的数据不会立马删除。
  2. kafka的consumer根据offset来确定从哪里开始消费。
  3. 如果一个Consumer失败,Consumer Group会找一个其它的Consumer,顶替它继续读取Partition。

具体过程为:

  1. kafka的consumer会从broker里面取出一批数据,数量由max.poll.records设置,给消费线程进行消费。
  2. 由于取出的一批消息数量太大,consumer在规定的时间之内没有消费完成,这个时间是max.poll.interval.ms设置的
  3. consumer coordinator 会由于没有接受到心跳而挂掉,并且自动提交offset失败(这时出现一些日志)
  4. Consumer Group会重新分配partition给其他的Consumer
  5. 由于自动提交offset失败,导致重新分配了partition的Consumer 又重新消费之前的一批数据
  6. 接着consumer重新消费,又出现了消费超时,无限循环下去。

链接:https://www.jianshu.com/p/98697293d827

解决方案:

  1. 增大max.poll.interval.ms,默认是300s,不推荐
  2. 减小max.poll.records,推荐
  3. 使用手动提交offset方法consumer.commitSync()/consumer.commitAsync(),在不确定数据情况下推荐
  4. 一个线程消费kafka数据,消费后将数据传给其他线程处理

Kafka的offset存在哪里?

Kafka版本[0.10.1.1],已默认将消费的 offset 迁入到了 Kafka 一个名为 __consumer_offsets 的Topic中。其实,早在 0.8.2.2 版本,已支持存入消费的 offset 到Topic中,只是那时候默认是将消费的 offset 存放在 Zookeeper 集群中。那现在,官方默认将消费的offset存储在 Kafka 的Topic中,同时,也保留了存储在 Zookeeper 的接口,通过 offsets.storage 属性来进行设置。

原文链接:https://blog.csdn.net/llflilongfei/article/details/81483509

Kafka为什么会出现数据丢失的情况

这是在ACK机制中,producer选择0、1的情况下会出现。

acks设置为0时,producer不和Kafka集群进行消息接受确认,当网络发生异常或者producer发生异常(producer异步发送消息的情况下死机)等情况时,存在消息丢失的可能;

acks设置为1时,Leader副本接收成功,Kafka集群就返回成功确认信息,而Follower副本可能还在同步。这时Leader副本突然出现异常,新Leader副本(原Follower副本)未能和其保持一致,就会出现消息丢失的情况。

转自链接:https://www.jianshu.com/p/f8f1d74a75cd

其他参考:https://blog.csdn.net/wanglei_storage/article/details/82692413

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值