1.kafka架构
kafka处理消息大概流程
- 生产者发送消息给kafka服务器
- 消费者从kafka服务器(broker)读取消息
- kafka服务器依靠zookeeper集群进行服务协调管理
2.kafka基本术语
-
message (消息)
生产消费的基本单位。
-
topic(主题)
代表逻辑上的一类消息,类似于其它消息队列中间件中的消息队列名称。 -
partition (分区)
topic的下一级,kafka为了提高吞吐量和可用性,会把一类消息分成多个区存储。topic、partition、message之间的关系如下图:
-
broker
代表一个kafka服务器节点 -
offset (消息位移值)
消息位移值一共有两种
a.第一种是分区内的每条消息都有一个位移值,代表每条消息在文件中的位置,offset从0到消息数量-1,就好比数组的下标。
b.第二种相对于kafka消费端而言的offset,代表了消费端当前的读取进度,比如消费端offset为3,代表消费者已经消费到了第四条消息。 -
replica (副本)
kafka为了实现高可用,会对partition(分区)保存多个replica(副本),存在的唯一理由就是为了实现消息的高可靠存储,不让消息丢失。
其中又分leader 副本和follower副本,follower同步leader副本,leader副本宕机时,从剩余follower副本中选出一个作为新的leader 副本,实现高可用(一个partition的多个副本一定不会在同一个broker上)。 -
ISR (in-sync replica,与leader replica保持同步的replica集合)
kafka会为每一个partition动态维护一个replica集合,该集合中的replica存储的所有消息日志与leader replica保持同步状态,如果因为网络延迟等原因部分ISR中的replica消息同步进度落后leader replica太多,则会将该replica踢出ISR,等后续追上进度时kafka再将其自动加入ISR。
3.kafka消息存储格式
-
上文提到topic是消息逻辑上的分类,类似于消息队列的队列名,为了解决消息的高可用和高可靠存储问题引入了partition,将一个partition生成多个副本分散在多台节点上实现高可用。topic是逻辑上的分类,partition是物理上的副本,如图,P1有多个副本分散在broker 1、broker 2、broker 3上,即使其中一台宕机,剩下的两个broker依然可以提供服务(broker 2 和 broker 3的P1_follower会不断从P1_leader进行消息同步,即使主节点宕机也能选举出一个节点为主节点继续服务,并保留截止到高水位的消息,高水位概念在下一篇关于kafka的高可靠存储会提到)。
-
broker的partition还可以继续细分为segement,每个partition在磁盘上对应着一个目录,目录名为topic名称+有序序号,例如名为test的topic下创建了三个partition,三个分区在/tmp/kafka-logs下的目录名分别为test-01、test-02、test-03。
-
如果进入test-01目录,会发现如下文件列表:
00000000000000000000.index
00000000000000000000.log
00000000000000170410.index
00000000000000170410.log
00000000000000239430.index
00000000000000239430.log
以上是segement文件列表,segement文件由索引文件(.index)和日志文件(.log)组成,大家会发现文件名是一个整数编号,20个整数数值组成,不足位数补零。第一个segement文件名从0开始,当log文件消息数量达到阈值,就会新建另一个segement文件,这个segement文件的名称是上一个segement log文件最后一条消息的offset位移值。
kafka为什么要这样设计segement文件?
假如我们把所有消息都存在一个文件里,查找一条offset为00000012564868789012的消息需要从头遍历岂不是等到黄花菜都凉了,可以看到这里用到二分查找进行优化,将需要查找的offset和segement的文件名进行二分查找就可以快速定位到我要找的消息在哪个文件里面,然后再从,index文件中获取到消息是在.log文件的哪个位置开始,哪个位置截止,将查找的时间复杂度从O(n)变为O(logn)。
partition目录下segement文件的设计,和mysql的b+树查找数据页、数据页内根据页目录进一步二分查找原理很相似,segement文件名相当于mysql的目录项页(枝干节点),而index文件相当于页内的页目录。