kafka如何实现高并发存储-如何找到一条需要消费的数据(阿里)
阿里太注重原理了:阿里问kafka如何实现高并发存储-如何找到一条需要消费的数据,kafka用了稀疏索引的方式,使用了二分查找法,其实很多索引都是二分查找法
二分查找法的时间复杂度:O(logn) redis,kafka,B+树的底层都采用了二分查找法
参考:二分查找法 redis的索引底层的 跳表原理 实现 聊聊Mysql索引和redis跳表 ---redis的跳表原理 时间复杂度O(logn)(阿里)
参考:二分查找法 mysql索引原理:一步步分析为什么B+树适合作为索引的结构 以及索引原理 (阿里面试)
参考:二分查找法:各种排序算法的时间复杂度和空间复杂度(阿里)
这是答案:
在partition中如何通过offset查找message
例如读取offset=368776的message,需要通过下面2个步骤查找。
第一步查找segment file
上述图2为例,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset **二分查找**文件列表,就可以快速定位到具体文件。
当offset=368776时定位到00000000000000368769.index|log
第二步通过segment file查找message
通过第一步定位到segment file,当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和 00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到 offset=368776为止。
从上述图3可知这样做的优点,segment index file采取稀疏索引存储方式,它减少索引文件大小,通过mmap可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。
具体参考:
Kafka 社区非常活跃,从 0.9 版本开始,Kafka 的标语已经从“一个高吞吐量,分布式的消息系统”改为"一个分布式流平台"。
Kafka 和传统的消息系统不同在于:
- Kafka是一个分布式系统,易于向外扩展。
- 它同时为发布和订阅提供高吞吐量。
- 它支持多订阅者,当失败时能自动平衡消费者。
- 消息的持久化。
Kafka 和其他消息队列的对比:
Kafka 架构原理
对于 Kafka 的架构原理,我们先提出如下几个问题:
- Kafka 的 topic 和分区内部是如何存储的,有什么特点?
- 与传统的消息系统相比,Kafka 的消费模型有什么优点?
- Kafka 如何实现分布式的数据存储与数据读取?
Kafka 架构图
Kafka 名词解释
在一套 Kafka 架构中有多个 Producer,多个 Broker,多个 Consumer,每个 Producer 可以对应多个 Topic,每个 Consumer 只能对应一个 Consumer Group。
整个 Kafka 架构对应一个 ZK 集群,通过 ZK 管理集群配置,选举 Leader,以及在 Consumer Group 发生变化时进行 Rebalance。
Topic 和 Partition
在 Kafka 中的每一条消息都有一个 Topic。一般来说在我们应用中产生不同类型的数据,都可以设置不同的主题。
一个主题一般会有多个消息的订阅者,当生产者发布消息到某个主题时,订阅了这个主题的消费者都可以接收到生产者写入的新消息。
Kafka 为每个主题维护了分布式的分区(Partition)日志文件,每个 Partition 在 Kafka 存储层面是 Append Log。
任何发布到此 Partition 的消息都会被追加到 Log 文件的尾部,在分区中的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,也就是我们的 Offset。Offset 是一个 Long 型的数字。
我们通过这个 Offset 可以确定一条在该 Partition 下的唯一消息。在 Partition 下面是保证了有序性,但是在 Topic 下面没有保证有序性。
在上图中我们的生产者会决定发送到哪个 Partition:
如果没有 Key 值则进行轮询发送。
如果有 Key 值,对 Key 值进行 Hash,然后对分区数量取余,保证了同一个 Key 值的会被路由到同一个分区;如果想队列的强顺序一致性,可以让所有的消息都设置为同一个 Key。
消费模型
消息由生产者发送到 Kafka 集群后,会被消费者消费。一般来说我们的消费模型有两种:
- 推送模型(Push)
- 拉取模型(Pull)
基于推送模型的消息系统,由消息代理记录消费状态。消息代理将消息推送到消费者后,标记这条消息为已经被消费,但是这种方式无法很好地保证消费的处理语义。
比如当我们已经把消息发送给消费者之后,由于消费进程挂掉或者由于网络原因没有收到这条消息,如果我们在消费代理将其标记为已消费,这个消息就***丢失了。
如果我们利用生产者收到消息后回复这种方法,消息代理需要记录消费状态,这种不可取。
如果采用 Push,消息消费的速率就完全由消费代理控制,一旦消费者发生阻塞,就会出现问题。
Kafka 采取拉取模型(Poll),由自己控制消费速度,以及消费的进度,消费者可以按照任意的偏移量进行消费。
比如消费者可以消费已经消费过的消息进行重新处理,或者消费最近的消息等等。
网络模型
Kafka Client:单线程 Selector
单线程模式适用于并发链接数小,逻辑简单,数据量小的情况。在 Kafka 中,Consumer 和 Producer 都是使用的上面的单线程模式。
这种模式不适合 Kafka 的服务端,在服务端中请求处理过程比较复杂,会造成线程阻塞,一旦出现后续请求就会无法处理,会造成大量请求超时,引起雪崩。而在服务器中应该充分利用多线程来处理执行逻辑。
Kafka Server:多线程 Selector
在 Kafka 服务端采用的是多线程的 Selector 模型,Acceptor 运行在一个单独的线程中,对于读取操作的线程池中的线程都会在 Selector 注册 Read 事件,负责服务端读取请求的逻辑。
成功读取后,将请求放入 Message Queue共享队列中。然后在写线程池中,取出这个请求,对其进行逻辑处理。
这样,即使某个请求线程阻塞了,还有后续的线程从消息队列中获取请求并进行处理,在写线程中处理完逻辑处理,由于注册了 OP_WIRTE 事件,所以还需要对其发送响应。
高可靠分布式存储模型
在 Kafka 中保证高可靠模型依靠的是副本机制,有了副本机制之后,就算机器宕机也不会发生数据丢失。
高性能的日志存储 kafka采用了稀疏索引的方式
Kafka 一个 Topic 下面的所有消息都是以 Partition 的方式分布式的存储在多个节点上。
同时在 Kafka 的机器上,每个 Partition 其实都会对应一个日志目录,在目录下面会对应多个日志分段(LogSegment)。
LogSegment 文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为 Segment 索引文件和数据文件。
先通过index文件,利用二分查找法,找到相应的稀疏索引,然后跟进index上的偏移量,找到log文件的位置,然后在log顺序遍历上面找到相应的文件;
2.4 在partition中如何通过offset查找message
例如读取offset=368776的message,需要通过下面2个步骤查找。
第一步查找segment file
上述图2为例,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset **二分查找**文件列表,就可以快速定位到具体文件。
当offset=368776时定位到00000000000000368769.index|log
第二步通过segment file查找message
通过第一步定位到segment file,当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和 00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到 offset=368776为止。
从上述图3可知这样做的优点,segment index file采取稀疏索引存储方式,它减少索引文件大小,通过mmap可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。
2.Kafka文件存储机制
Kafka部分名词解释如下:
Broker:消息中间件处理结点,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群。
Topic:一类消息,例如page view日志、click日志等都可以以topic的形式存在,Kafka集群能够同时负责多个topic的分发。
Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。
Segment:partition物理上由多个segment组成,下面2.2和2.3有详细说明。
offset:每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息.
分析过程分为以下4个步骤:
topic中partition存储分布
partiton中文件存储方式
partiton中segment文件存储结构
在partition中如何通过offset查找message
通过上述4过程详细分析,我们就可以清楚认识到kafka文件存储机制的奥秘。
2.1 topic中partition存储分布
假设实验环境中Kafka集群只有一个broker,xxx/message-folder为数据文件存储根目录,在Kafka broker中server.properties文件配置(参数log.dirs=xxx/message-folder),例如创建2个topic名称分别为report_push、launch_info, partitions数量都为partitions=4(将一个topic分为4个部分存储)
存储路径和目录规则为:
xxx/message-folder
|--report_push-0
|--report_push-1
|--report_push-2
|--report_push-3
|--launch_info-0
|--launch_info-1
|--launch_info-2
|--launch_info-3
在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。
如果是多broker分布情况,请参考文末kafka集群partition分布原理分析
2.2 partiton中文件存储方式
下面示意图形象说明了partition中文件存储方式:
图1
每个partion(目录)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除。
每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。
这样做的好处就是能快速删除无用文件,有效提高磁盘利用率。
2.3 partiton中segment文件存储结构
读者从2.2节了解到Kafka文件系统partition存储方式,本节深入分析partion中segment file组成和物理结构。
segment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件.
segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。
下面文件列表是笔者在Kafka broker上做的一个实验,创建一个topicXXX包含1 partition,设置每个segment大小为500MB,并启动producer向Kafka broker写入大量数据,如下图2所示segment文件列表形象说明了上述2个规则:
以上述图2中一对segment file文件为例,说明segment中index<—->data file对应关系物理结构如下:
上述图3中索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。
其中以索引文件中元数据3,497为例,依次在数据文件中表示第3个message(在全局partiton表示第368772个message)、以及该消息的物理偏移地址为497。
从上述图3了解到segment data file由许多message组成,下面详细说明message物理结构如下:
图4
参数说明:
关键字 | 解释说明 |
---|---|
8 byte offset | 在parition(分区)内的每条消息都有一个有序的id号,这个id号被称为偏移(offset),它可以唯一确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message |
4 byte message size | message大小 |
4 byte CRC32 | 用crc32校验message |
1 byte “magic" | 表示本次发布Kafka服务程序协议版本号 |
1 byte “attributes" | 表示为独立版本、或标识压缩类型、或编码类型。 |
4 byte key length | 表示key的长度,当key为-1时,K byte key字段不填 |
K byte key | 可选 |
value bytes payload | 表示实际消息数据。 |
实验环境:
Kafka集群:由2台虚拟机组成
cpu:4核
物理内存:8GB
网卡:千兆网卡
jvm heap: 4GB
详细Kafka服务端配置及其优化请参考:kafka server.properties配置详解
图5
从上述图5可以看出,Kafka运行时很少有大量读磁盘的操作,主要是定期批量写磁盘操作,因此操作磁盘很高效。这跟Kafka文件存储中读写message的设计是息息相关的。Kafka中读写message有如下特点:
写message
消息从java堆转入page cache(即物理内存)。
由异步线程刷盘,消息从page cache刷入磁盘。
读message
消息直接从page cache转入socket发送出去。
当从page cache没有找到相应数据时,此时会产生磁盘IO,从磁
盘Load消息到page cache,然后直接从socket发出去
4.总结
Kafka高效文件存储设计特点
Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
通过索引信息可以快速定位message和确定response的最大大小。
通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。
topic------->多个partiton----------->1个partion多个segment----------->1个segment多个index和log
在只有一个broker的时候,多个partion位于这个broker,有多个broker的时候是按照一定的算法分布在多个broker上。
5.kafka中的partition和offset
5.1Log机制
说到分区,就要说kafka对消息的存储.在官方文档中.
分区读写日志图
首先,kafka是通过log(日志)来记录消息发布的.每当产生一个消息,kafka会记录到本地的log文件中,这个log和我们平时的log有一定的区别.这里可以参考一下The Log,不多解释.
这个log文件默认的位置在config/server.properties中指定的.默认的位置是log.dirs=/tmp/kafka-logs,linux不用说,windows的话就在你对应磁盘的根目录下.我这里是D盘.
分区partition
kafka是为分布式环境设计的,因此如果日志文件,其实也可以理解成消息数据库,放在同一个地方,那么必然会带来可用性的下降,一挂全挂,如果全量拷贝到所有的机器上,那么数据又存在过多的冗余,而且由于每台机器的磁盘大小是有限的,所以即使有再多的机器,可处理的消息还是被磁盘所限制,无法超越当前磁盘大小.因此有了partition的概念.
kafka对消息进行一定的计算,通过hash来进行分区.这样,就把一份log文件分成了多份.如上面的分区读写日志图,分成多份以后,在单台broker上,比如快速上手中,如果新建topic的时候,我们选择了--replication-factor 1 --partitions 2,那么在log目录里,我们会看到
test-0目录和test-1目录.就是两个分区了.
你可能会想,这特么没啥区别呀.注意,当有了多个broker之后,这个意义就存在了.这里上一张图,原文在参考链接里有
5.2 kafka分布式分区存储
这是一个topic包含4个Partition,2 Replication(拷贝),也就是说全部的消息被放在了4个分区存储,为了高可用,将4个分区做了2份冗余,然后根据分配算法.将总共8份数据,分配到broker集群上.
结果就是每个broker上存储的数据比全量数据要少,但每份数据都有冗余,这样,一旦一台机器宕机,并不影响使用.比如图中的Broker1,宕机了.那么剩下的三台broker依然保留了全量的分区数据.所以还能使用,如果再宕机一台,那么数据不完整了.当然你可以设置更多的冗余,比如设置了冗余是4,那么每台机器就有了0123完整的数据,宕机几台都行.需要在存储占用和高可用之间做衡量.
至于宕机后,zookeeper会选出新的partition leader.来提供服务.这个等下篇文章
偏移offset
上一段说了分区,分区就是一个有序的,不可变的消息队列.新来的commit log持续往后面加数据.这些消息被分配了一个下标(或者偏移),就是offset,用来定位这一条消息.
消费者消费到了哪条消息,是保持在消费者这一端的.消息者也可以控制,消费者可以在本地保存最后消息的offset,并间歇性的向zookeeper注册offset.也可以重置offset
如何通过offset算出分区
其实partition存储的时候,又分成了多个segment(段),然后通过一个index,索引,来标识第几段.这里先可以去看一下本地log目录的分区文件夹.
在我这里,test-0,这个分区里面,会有一个index文件和一个log文件,
index和log
对于某个指定的分区,假设每5个消息,作为一个段大小,当产生了10条消息的情况想,目前有会得到(只是解释)
0.index (表示这里index是对0-4做的索引)
5.index (表示这里index是对5-9做的索引)
10.index (表示这里index是对10-15做的索引,目前还没满)
和
0.log
5.log
10.log
,当消费者需要读取offset=8的时候,首先kafka对index文件列表进行二分查找,可以算出.应该是在5.index对应的log文件中,然后对对应的5.log文件,进行顺序查找,5->6->7->8,直到顺序找到8就好了.
以上是Kafka文件存储机制及partition和offset的全部内容,在云栖社区的博客、问答、云栖号、人物、课程等栏目也有Kafka文件存储机制及partition和offset的相关内容,欢迎继续使用右上角搜索按钮进行搜索存储 , 文件 , 数据 , 索引 , 磁盘 物理 kafka offset保存机制、kafka offset 存储、kafka partition、kafka partition 设置、kafka partition 数量,以便于您获取更多的相关知识。
副本机制
Kafka 的副本机制是多个服务端节点对其他节点的主题分区的日志进行复制。
当集群中的某个节点出现故障,访问故障节点的请求会被转移到其他正常节点(这一过程通常叫 Reblance)。
Kafka 每个主题的每个分区都有一个主副本以及 0 个或者多个副本,副本保持和主副本的数据同步,当主副本出故障时就会被替代。
在 Kafka 中并不是所有的副本都能被拿来替代主副本,所以在 Kafka 的 Leader 节点中维护着一个 ISR(In Sync Replicas)集合。
翻译过来也叫正在同步中集合,在这个集合中的需要满足两个条件:
- 节点必须和 ZK 保持连接。
- 在同步的过程中这个副本不能落后主副本太多。
另外还有个 AR(Assigned Replicas)用来标识副本的全集,OSR 用来表示由于落后被剔除的副本集合。
所以公式如下:ISR = Leader + 没有落后太多的副本;AR = OSR+ ISR。
这里先要说下两个名词:HW(高水位)是 Consumer 能够看到的此 Partition 的位置,LEO 是每个 Partition 的 Log ***一条 Message 的位置。
HW 能保证 Leader 所在的 Broker 失效,该消息仍然可以从新选举的 Leader 中获取,不会造成消息丢失。
当 Producer 向 Leader 发送数据时,可以通过 request.required.acks 参数来设置数据可靠性的级别:
- 1(默认):这意味着 Producer 在 ISR 中的 Leader 已成功收到的数据并得到确认后发送下一条 Message。如果 Leader 宕机了,则会丢失数据。
- 0:这意味着 Producer 无需等待来自 Broker 的确认而继续发送下一批消息。这种情况下数据传输效率***,但是数据可靠性却是***的。
- -1:Producer 需要等待 ISR 中的所有 Follower 都确认接收到数据后才算一次发送完成,可靠性***。
但是这样也不能保证数据不丢失,比如当 ISR 中只有 Leader 时(其他节点都和 ZK 断开连接,或者都没追上),这样就变成了 acks = 1 的情况。
高可用模型及幂等
在分布式系统中一般有三种处理语义:
at-least-once
至少一次,有可能会有多次。如果 Producer 收到来自 Ack 的确认,则表示该消息已经写入到 Kafka 了,此时刚好是一次,也就是我们后面的 Exactly-once。
但是如果 Producer 超时或收到错误,并且 request.required.acks 配置的不是 -1,则会重试发送消息,客户端会认为该消息未写入 Kafka。
如果 Broker 在发送 Ack 之前失败,但在消息成功写入 Kafka 之后,这一次重试将会导致我们的消息会被写入两次。
所以消息就不止一次地传递给最终 Consumer,如果 Consumer 处理逻辑没有保证幂等的话就会得到不正确的结果。
在这种语义中会出现乱序,也就是当***次 Ack 失败准备重试的时候,但是第二消息已经发送过去了,这个时候会出现单分区中乱序的现象。
我们需要设置 Prouducer 的参数 max.in.flight.requests.per.connection,flight.requests 是 Producer 端用来保存发送请求且没有响应的队列,保证 Produce r端未响应的请求个数为 1。
at-most-once
如果在 Ack 超时或返回错误时 Producer 不重试,也就是我们讲 request.required.acks = -1,则该消息可能最终没有写入 Kafka,所以 Consumer 不会接收消息。
exactly-once
刚好一次,即使 Producer 重试发送消息,消息也会保证最多一次地传递给 Consumer。该语义是最理想的,也是最难实现的。
在 0.10 之前并不能保证 exactly-once,需要使用 Consumer 自带的幂等性保证。0.11.0 使用事务保证了。
如何实现 exactly-once
要实现 exactly-once 在 Kafka 0.11.0 中有两个官方策略:
单 Producer 单 Topic
每个 Producer 在初始化的时候都会被分配一个唯一的 PID,对于每个唯一的 PID,Producer 向指定的 Topic 中某个特定的 Partition 发送的消息都会携带一个从 0 单调递增的 Sequence Number。
在我们的 Broker 端也会维护一个维度为,每次提交一次消息的时候都会对齐进行校验:
- 如果消息序号比 Broker 维护的序号大一以上,说明中间有数据尚未写入,也即乱序,此时 Broker 拒绝该消息,Producer 抛出 InvalidSequenceNumber。
- 如果消息序号小于等于 Broker 维护的序号,说明该消息已被保存,即为重复消息,Broker 直接丢弃该消息,Producer 抛出 DuplicateSequenceNumber。
- 如果消息序号刚好大一,就证明是合法的。
上面所说的解决了两个问题:
- 当 Prouducer 发送了一条消息之后失败,Broker 并没有保存,但是第二条消息却发送成功,造成了数据的乱序。
- 当 Producer 发送了一条消息之后,Broker 保存成功,Ack 回传失败,Producer 再次投递重复的消息。
上面所说的都是在同一个 PID 下面,意味着必须保证在单个 Producer 中的同一个 Seesion 内,如果 Producer 挂了,被分配了新的 PID,这样就无法保证了,所以 Kafka 中又有事务机制去保证。
事务
在 Kafka 中事务的作用是:
- 实现 exactly-once 语义。
- 保证操作的原子性,要么全部成功,要么全部失败。
- 有状态的操作的恢复。
事务可以保证就算跨多个,在本次事务中的对消费队列的操作都当成原子性,要么全部成功,要么全部失败。
并且,有状态的应用也可以保证重启后从断点处继续处理,也即事务恢复。
在 Kafka 的事务中,应用程序必须提供一个唯一的事务 ID,即 Transaction ID,并且宕机重启之后,也不会发生改变。
Transactin ID 与 PID 可能一一对应,区别在于 Transaction ID 由用户提供,而 PID 是内部的实现对用户透明。
为了 Producer 重启之后,旧的 Producer 具有相同的 Transaction ID 失效,每次 Producer 通过 Transaction ID 拿到 PID 的同时,还会获取一个单调递增的 Epoch。
由于旧的 Producer 的 Epoch 比新 Producer 的 Epoch 小,Kafka 可以很容易识别出该 Producer 是老的,Producer 并拒绝其请求。
为了实现这一点,Kafka 0.11.0.0 引入了一个服务器端的模块,名为 Transaction Coordinator,用于管理 Producer 发送的消息的事务性。
该 Transaction Coordinator 维护 Transaction Log,该 Log 存于一个内部的 Topic 内。
由于 Topic 数据具有持久性,因此事务的状态也具有持久性。Producer 并不直接读写 Transaction Log,它与 Transaction Coordinator 通信,然后由 Transaction Coordinator 将该事务的状态插入相应的 Transaction Log。
Transaction Log 的设计与 Offset Log 用于保存 Consumer 的 Offset 类似。
***
关于消息队列或者 Kafka 的一些常见的面试题,通过上面的文章可以提炼出以下几个比较经典的问题,大部分问题都可以从上面总结后找到答案:
- 为什么使用消息队列?消息队列的作用是什么?
- Kafka 的 Topic 和分区内部是如何存储的,有什么特点?
- 与传统的消息系统相比,Kafka 的消费模型有什么优点?
- Kafka 如何实现分布式的数据存储与数据读取?
- Kafka 为什么比 RocketMQ 支持的单机 Partition 要少?
- 为什么需要分区,也就是说主题只有一个分区,难道不行吗?
- 日志为什么需要分段?
- Kafka 是依靠什么机制保持高可靠,高可用?
- 消息队列如何保证消息幂等?
- 让你自己设计个消息队列,你会怎么设计,会考虑哪些方面?
kafka工作原理
- http://www.aboutyun.com/thread-11895-1-1.html
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转):http://www.cnblogs.com/likehua/p/3999538.html
- apache kafka系列之在zookeeper中存储结构:http://blog.csdn.net/strawbingo/article/details/45484139
- Kafka文件存储机制那些事:http://www.open-open.com/lib/view/open1421150566328.html
- apache kafka系列之server.properties配置文件参数说明:http://blog.csdn.net/lizhitao/article/details/25667831
- kafka入门:http://bit1129.iteye.com/blog/2174791
- Kafka 设计与原理详解:http://blog.csdn.net/suifeng3051/article/details/48053965
Kafka系统的角色
- Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic
- topic: 可以理解为一个MQ消息队列的名字
- Partition:
- 为了实现扩展性,一个非常大的topic可以分布到多个 broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
- partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体 (多个partition间)的顺序。
- 也就是说,一个topic在集群中可以有多个partition,那么分区的策略是什么?(消息发送到哪个分区上,有两种基本的策略,一是采用Key Hash算法,一是采用Round Robin算法)
- Offset:kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka
- Producer :消息生产者,就是向kafka broker发消息的客户端。
- Consumer :消息消费者,向kafka broker取消息的客户端
- Consumer Group (CG):
- 消息系统有两类,一是广播,二是订阅发布。广播是把消息发送给所有的消费者;发布订阅是把消息只发送给订阅者。Kafka通过Consumer Group组合实现了这两种机制: 实现一个topic消息广播(发给所有的consumer)和单播(发给任意一个consumer)。一个topic可以有多个CG。
- topic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个CG只会把消息发给该CG中的一个 consumer(这是实现一个Topic多Consumer的关键点:为一个Topic定义一个CG,CG下定义多个Consumer)。如果需要实现广播,只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。
- 典型的应用场景是,多个Consumer来读取一个Topic(理想情况下是一个Consumer读取Topic的一个Partition),那么可以让这些Consumer属于同一个Consumer Group即可实现消息的多Consumer并行处理,原理是Kafka将一个消息发布出去后,ConsumerGroup中的Consumers可以通过Round Robin的方式进行消费(Consumers之间的负载均衡使用Zookeeper来实现)
A two server Kafka cluster hosting four partitions (P0-P3) with two consumer groups. Consumer group A has two consumer instances and group B has four.
Topic、Partition和Replica的关系
如上图,一个Topic有四个Partition,每个Partition两个replication。
Zookeeper在Kakfa中扮演的角色Kafka将元数据信息保存在Zookeeper中,但是发送给Topic本身的数据是不会发到Zk上的,否则Zk就疯了。
- kafka使用zookeeper来实现动态的集群扩展,不需要更改客户端(producer和consumer)的配置。broker会在zookeeper注册并保持相关的元数据(topic,partition信息等)更新。
- 而客户端会在zookeeper上注册相关的watcher。一旦zookeeper发生变化,客户端能及时感知并作出相应调整。这样就保证了添加或去除broker时,各broker间仍能自动实现负载均衡。这里的客户端指的是Kafka的消息生产端(Producer)和消息消费端(Consumer)
- Broker端使用zookeeper来注册broker信息,以及监测partition leader存活性。
- Consumer端使用zookeeper用来注册consumer信息,其中包括consumer消费的partition列表等,同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息。
- Zookeeper和Producer没有建立关系,只和Brokers、Consumers建立关系以实现负载均衡,即同一个Consumer Group中的Consumers可以实现负载均衡。
问题
- Topic有多个Partition,那么消息分配到某个Partition的依据是什么?
- Key Hash或者Round Robin
- 如何查看一个Topic有多少个Partition?
- 使用kakfa-topic.sh --list topic topicName --zookeeper zookeeper.servers.list
zookeeper存储结果
[zk: localhost:2181(CONNECTED) 0] ls /
[admin, consumers, config, brokers]
参考:kafka工作原理
参考:Kafka文件存储机制及partition和offset
kafka如何实现高并发存储-如何找到一条需要消费的数据(阿里)的更多相关文章
- 2020-05-25:MQ应用场景、Kafka和rabbit区别?kafka为什么支撑高并发? 来自
福哥答案2020-05-25: 应用场景:解耦.异步.削峰.区别如下:特性 ActiveMQ RabbitMQ RocketMQ Kafka单机吞吐量 万级,比 RocketMQ.Kafka 低一个数 ...
- [置顶]
flume高并发优化——(14)解决空行停止收集数据问题,及offsets变小问题
日志平台运行一段时间,发现日志有部分丢失,通过检查日志,发现有两个问题导致数据丢失,一个是遇到空行后,日志停止收集,还有就是kafka监控offsets时变小,通过分析代码,找到如下方法: 空行问 ...
- redis为何单线程 效率还这么高 为何使用跳表不使用B+树做索引(阿里)
如果想了解 redis 与Memcache的区别参考:Redis和Memcache的区别总结 阿里的面试官问问我为何redis 使用跳表做索引,却不是用B+树做索引 因为B+树的原理是 叶子节点存储数 ...
- 达达O2O后台架构演进实践:从0到4000高并发请求背后的努力
1.引言 达达创立于2014年5月,业务覆盖全国37个城市,拥有130万注册众包配送员,日均配送百万单,是全国领先的最后三公里物流配送平台. 达达的业务模式与滴滴以及Uber很相似,以众包的方式利 ...
- 如何保证Redis的高并发
单机的redis几乎不太可能说QPS超过10万+,一般在几万. 除非一些特殊情况,比如你的机器性能特别好,配置特别高,物理机,维护做的特别好,而且你的整体的操作不是太复杂. Redis通过主从架构,实 ...
- redis高可用,保证高并发
目录 redis如何通过读写分离来承载读请求QPS超过10万+ redis replication以及master持久化对主从架构的安全意义 redis主从复制原理.断点续传.无磁盘化复制.过期key ...
- 关于Redis的几件小事 | 高并发和高可用
如果你用redis缓存技术的话,肯定要考虑如何用redis来加多台机器,保证redis是高并发的,还有就是如何让Redis保证自己不是挂掉以后就直接死掉了. redis高并发:主从架构,一主多从,一般 ...
- HttpClient在高并发场景下的优化实战
在项目中使用HttpClient可能是很普遍,尤其在当下微服务大火形势下,如果服务之间是http调用就少不了跟http客户端找交道.由于项目用户规模不同以及应用场景不同,很多时候可能不需要特别处理也. ...
- Springcloud 微服务 高并发(实战1):第1版秒杀
疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列之15 [博客园总入口 ] 前言 前言 疯狂创客圈(笔者尼恩创建的高并发研习社群)Springcloud 高并发系列文章,将为大家介绍三个版 ...
随机推荐
- C++ 中的静态成员函数与静态成员变量
于CSDN 2014-01-17 与静态数据成员一样,静态成员函数是类的一部分,而不是对象的一部分.如果要在类外调用公用的静态成员函数,要用类名和域运算符"∷".如Box∷volu ...
- js根据json数组多个字段排序
/**数组根据数组对象中的某个属性值进行排序的方法 * 使用例子:newArray.sort(sortByArr(['number'],false)) //表示根据number属性降序排列;若第二个参 ...
- Python 之 计算psnr和ssim值
基于python版的PSNR和ssim值计算 总所周知,图像质量评价的常用指标有PSNR和SSIM等,本博文是基于python版的图像numpy的float64格式和uint8格式计算两种指标值(附代 ...
- android黑白屏的问题
你会很奇怪,为什么有些app启动时,会出现一会儿的黑屏或者白屏才进入Activity的界面显示,但是有些app却不会如QQ手机端,的确这里要做处理一下.这里先了解一下为什么会出现这样的现象,其实很简单 ...
- react的事件处理为什么要bind this 改变this的指向?
react的事件处理会丢失this,所以需要绑定,为什么会丢失this? 首先来看摘自官方的一句话: You have to be careful about the meaning of this ...
- Java JDBC 操作二进制数据、日期时间
二进制数据 mysql提供了四种类型来存储二进制数据: TinyBlob 最多可存储255字节 Blob 最多可存储65KB MediumBlob 最多可存储16MB LongBlob ...
- 15 Android系统安全(简要)
Android的用户和第三方软件,Android组件和数据安全 手机root后的问题:1.系统不稳定,2.病毒入侵,3.数据泄露 Root原理: Root分两类:1.临时root,2.永久root R ...
- Dalvik虚拟机结构——1
Dalvik核心内容:libdvm.so 主要有C语言实现,依赖于Linux内核的一部分功能:线程机制,内存管理机制,每一个Android应有都对应一个dalvik实例 Dalvik虚拟机功能:主要 ...
- C# 7可以在.NET Framework 4上运行吗?
https://stackoverflow.com/questions/42482520/does-c-sharp-7-0-work-for-net-4-5 To sum up: All of C# ...
- Linux下设置Nginx开机自启
1.本地环境 [root@dev ~]#cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) 2.在/etc/init.d创建ngi ...