浅谈Kakfa【一】Kafka基本概述与Partition

一、前言

本系列是Kafka基本知识与原理解读,不涉及源码的翻译与解释,可以全面了解Kafka的相关概念与底层实现原理。

1.为什么引入MQ?

(1) 解耦:将两个或多个互相强关联的系统进行解耦,可以独立修改或者扩展各自的处理过程,只要确保它们遵守相同的接口约束即可。

(2) 异步:有时我们对于某些逻辑或者消息不希望立刻进行处理,那么通过消息队列进行异步处理非常合适,只要往队列中SET消息即可,无需通过数据库和定时任务来进行异步处理。

(3) 削峰:当我们系统在短时间需要抵挡住更大的请求量时,往往不一定需要我们第一时间处理任务,可以将非核心逻辑或者异步逻辑通过消息队列存储等待后续慢慢处理,以提供更高的并发处理量。

2.哪些场景需要MQ?

以下列举三个场景,其实MQ使用的场景非常非常多。

(1)通知:异步系统通知通常使用MQ。
(2)消息监听:比如说监听数据库Binlog。
(3)延迟任务处理:比如说发送一个消息,半小时后再进行处理。

3.使用MQ需要注意哪些?

(1)链路变长:消息的传递路径变得更长,延时会增加。
(2)无感知:上下游无法感知消息处理的结果。
(3)消息可靠性:如何保证消息一定消费成功/发送成功。
(4)消息是否需要顺序:是否需要保证消息的处理顺序。

二、Kafka概述与日志存储结构

Kafka 是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。

图片来源网路
Kafka模型(来源网络)

1.Kafka名词解释:

(1)Producer:消息生产者,就是向 Kafka broker 发消息的客户端。
(2)Consumer:消息消费者,向 Kafka broker 拉取消息的客户端。
(3)Consumer Group: 消费者组,由多个 consumer 组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费; 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
(4)Broker:一台 Kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker可以容纳多个 topic。
(5)Topic:可以理解为一个队列,生产者和消费者面向的都是一个 topic。
(6)Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。
partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多 个partition间)的顺序。
(7)Replica:副本,为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka 仍然能够继续工作,kafka 提供了副本机制,一 个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower。
(8)Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 leader。
(9)Follower:每个分区多个副本中的“从”,实时从 leader 中同步数据,保持和 leader 数据的同步。leader 发生故障时,某个 follower 会成为新的 follower。
(10)Offset :用来标记每个Topic已经消费到的消息位置。
(11)ISR : 在高可用模式下与Leader保证同步的副本列表。
(12)AR : 所有副本,无论是否与Leader保持同步。
(13)HW:高水位,用来计算消费者最多可以消费到的位置。
(14)LEO: 同步偏移量,用来标识每个副本的Offset同步位置,用来计算HW位置。

2.Kafka特性:

1)多个生产者和多个消费者
Kafak可以无缝的支持多个生产者,也支持多个消费者从一个单独的消息流中读取数据,且各个消费者之间互不影响。
2)可持久化操作
Kafka还允许消费者非实时读取消息,因为Kafka将消息按一定顺序持久化到磁盘,保证了数据不会丢失,磁盘线性写的速度约是随机写的速度的6000多 倍(PS:官网说的),而且以时间复杂度为O(1)的方式提供消息持久化能力,对TB级以上的数据也能保证常数时间的访问性能。
3)高吞吐量
高吞吐量是KafKa设计的主要目标,KafKa将数据写到磁盘,充分利用磁盘的顺序读写。同时KafKa在数据写入以及数据同步采用了零拷贝(zero-copy) 技术,采用sendFile()函数调用,该函数是在两个文件描述符之间直接传递数据,完全在内核中操作,从而避免了内核缓冲区与用户缓冲区之间数据的 拷贝,操作效率极高。KafKa还支持数据压缩以及批量发送,同时KafKa将每个主题划分为多个分区,这一系列的优化以及实现方法使得KafKa具有很高的 吞吐量。
KafKa支持每秒钟数百万级别的消息;
4)可伸缩性
KafKa依赖ZooKeeper来对集群进行协调管理,这样使得KafKa更加容易进行水平扩展。生产者、消费者和代理都为分布式,可以配置多个。同时在机器扩 展的时候无需将整个集群停机,集群可以自动的感知,重新进行负载均衡以及数据复制;
5)实时性
由于Kafka可横向扩展生产者、消费者、broker,使得集群可以轻松处理巨大的消息流,在处理大量数据的同时,还能保证亚秒级的消息延迟,实时性极 高。
6)容错性
Kafka消息都会在集群中进行备份,每个分区都有一台server作为leader,其他server作为follwers,当leader宕机了,follower中的一台server会自动 成为新的leader,继续有条不紊的工作,所以容错性很高且集群的负载是平衡的。
7)多客户端支持
KafKa支持多种连接器和多种语言接入,与当前大多数主流的数据框架都可以很好的集成;

3.Kafka日志存储:

Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相互独立的。每个topic又可以分成几个不同的partition(每个topic有几个partition是 在创建topic时指定的),每个partition存储一部分Message。借用官方的一张图,可以直观地看到topic和partition的关系。
在这里插入图片描述
partition是以文件的形式存储在文件系统中,比如,创建了一个名为page_visits的topic,其有5个partition,那么在Kafka的数据目录中(由配置文件中的 log.dirs指定的)中就有这样5个目录: page_visits-0, page_visits-1,page_visits-2,page_visits-3,page_visits-4,其命名规则为<topic_name>- <partition_id>,里面存储的分别就是这5个partition的数据。
接下来,我们来分析partition目录中的文件的存储格式和相关的代码所在的位置。

3.1 Partition的数据文件

Partition中的每条Message由offset来表示它在这个partition中的偏移量,这个offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了partition中的一条Message。
因此,可以认为offset是partition中Message的id。partition中的每条Message包含了以下三个属性:

  • offset
  • MessageSize
  • data

其中offset为long型,MessageSize为int32,表示data有多大,data为message的具体内容。 Partition的数据文件则包含了若干条上述格式的Message,按offset由小到大排列在一起。它的实现类为FileMessageSet,类图如下:
在这里插入图片描述
它的主要方法如下:

  • append: 把给定的ByteBufferMessageSet中的Message写入到这个数据文件中。
  • searchFor: 从指定的startingPosition开始搜索找到第一个Message其offset是大于或者等于指定的offset,并返回其在文件中的位置Position。 它的实现方式是从startingPosition开始读取12个字节,分别是当前MessageSet的offset和size。如果当前offset小于指定的offset,那么将 position向后移动LogOverHead+MessageSize(其中LogOverHead为offset+messagesize,为12个字节)。
  • read:准确名字应该是slice,它截取其中一部分返回一个新的FileMessageSet。它不保证截取的位置数据的完整性。
  • sizeInBytes: 表示这个FileMessageSet占有了多少字节的空间。
  • truncateTo: 把这个文件截断,这个方法不保证截断位置的Message的完整性。
  • readInto: 从指定的相对位置开始把文件的内容读取到对应的ByteBuffer中。
3.2 数据文件的分段

Kafka解决查询效率的手段之一是将数据文件分段,日志分段是Log目录下的小文件,主要包括日志文件(.log)、偏移量索引文件(.index)、时间戳索引文件(.timeindex)。
在这里插入图片描述

3.3 数据文件的索引

数据文件分段使得可以在一个较小的数据文件中查找对应offset的Message了,但是这依然需要顺序扫描才能找到对应offset的Message。为了进一步提高查 找的效率,Kafka为每个分段后的数据文件建立了两个索引文件。偏移量索引文件用来建立消息偏移量(offset )到物理地址之间的映射关系,方便快速定位 消息所在的物理文件位置;时间戳索引文件则根据指定的时间戳(timestamp)来查找对应的偏移量信息。

3.4 偏移量索引

偏移量索引文件的格式如下图红色框注,索引文件中包含若干个索引条目,每个条目表示数据文件中一条Message的索引。索引包含两个部分(均为4个字节 的数字),分别为相对offset和position。分别表示相对偏移量和物理位置

  • 相对offset:因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条Message相对于其所属数据文件中最小的offset的大小。 举例,分段后的一个数据文件的offset是从20开始,那么其文件名就是00000000000000000020.log ,offset为25的Message在index文件中的 相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。
  • position,表示该条Message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的Message了。

在这里插入图片描述
我们知道偏移量索引文件的存在是为了提高查找效率,那么kafka具体是如何实现的呢?
其实,Kafka的每个日志对象中用ConcurrentSkipListMap(上图)的结构保存着各个日志分段,该结构使用跳表1保存各日志文件的baseOffset,这样查找 baseOffset的时间复杂度便是O(logn);使用Map保存baseOffset到日志索引文件的映射,时间复杂度O(1)。如此便可快速定位的日志索引文件。

举个例子:
当我们要查找一个偏移量为268的消息时

  1. 首先,通过跳表找到第一个不大于268的baseOffset,定位的251的日志分段;
  2. 其次,计算相对偏移量relativeOffset = 268-251=17; 第三,再在对应的偏移量索引文件中找到第一个不大于17的relativeOffset,此处是14;
  3. 最后,根据14对应的position(459)定位到具体日志分段文件位置开始顺序查找目标消息。
3.6 时间戳索引

时间戳索引文件的格式如下图红色框注,每条记录包括timestamp和taltiveOffset两部分,分别表示当前日志分段的最大时间戳(占8字节)和时间戳对应的 相对偏移量(占4字节)。时间戳索引文件中的时间戳保持单调递增,追加的时间戳索引项中tiemstamp必须大于之前的tiemstamp,否则不予追加。
在这里插入图片描述
我们己经知道每当写入一定量的消息时,就会在偏移量索引文件和时间戳索引文件中分别增加一个偏移量索引项和时间戳索引项。两个文件增加索引项的操 作是同时进行的,但并不意味着偏移量索引中的relativeOffset和时间戳索引项中的relativeOffset是同一个值。
当我们查找指定时间开始的消息时,便会用到时间戳索引文件。

三、结语

本文内容并非本人原创,此系列文章产出于团队分享,分享人也是集合了网络上各部分知识汇总,由于无法找到具体的出处,如果原作者看到留言,将会更新来源出处,此处@本文原创者 @FSL

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值