Kafka基本概念整理

之前在项目中做过一些采集相关的开发工作,最近又使用了公司自研的组件进行数据的采集,虽然自研的组件对kafka进行了一些封装,可以不必关注底层细节了。但是了解下底层原理还是有必要的。

所以这里对之前的相关开发工作做一些总结,并梳理下遇到的一些问题。

 

一、Kafka介绍

1)Apache Kafka由Java和Scala编写,是由Apache软件基金会开发的一个开源消息系统项目。

2)Kafka最初是由LinkedIn公司开发,并于2011年初开源。2012年10月从Apache Incubator毕业。该项目的目标是为处理实时数据提供一个统一、高通量、低等待的平台。

3)Kafka是一个分布式消息队列。Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个节点组成,每个节点(server)称为broker。

4)Kafka集群依赖于zookeeper集群来保存一些meta信息,并保证系统可用性。

 

二、消息队列模式

1.点对点模式(一对一,消费者主动拉取数据)

点对点模式通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。

2.发布/订阅模式(一对多,数据生产后,推送给所有的订阅者)

发布订阅模型则是一个基于推送的消息传送模型,发布订阅模型可以有多个不同的订阅者,临时订阅者只有在主动监听主题时才接受消息,而持久订阅者则监听主题的所有消息,即使当前订阅者不可用,处于离线状态。

 

三、Kafka架构图

Kafka基本概念整理_第1张图片

1)Producer :消息生产者,就是向kafka broker发消息的客户端。

2)Consumer :消息消费者,向kafka broker取消息的客户端

3)Topic :可以理解为一个队列。

4)Consumer Group :一个ConsumerGroup里面可以有一个或者多个Consumer,一个Topic每个Partition的数据只能被一个Consumer消费。一个Consumer可以消费多个Partition的数据,反过来不行。

5)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。

6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。Partition中的每条消息都会被分配一个有序的id(offset)。Kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序

7)Offset:每条消息被添加到分区时,都会被分配一个offset,它是消息在分区中的唯一编号,kafka通过offset保证消息在分区内的顺序。

 

四、Kafka写入数据流程、日志存储、副本及ISR

数据写入和Partition指定:

Topic是一个逻辑上的概念,物理上会被分成多个Partition,每个Partition都可以有多个Replic或者没有。Producer往某个Topic中发送数据的时候,数据会按照一定的规则追加到各个Partition中,选择Partition规则如下:

1.指定了Partition,则直接使用;

2.未指定Partition,但存在key,通过key的value进行hash出一个Partition。

3. Partition和key都未指定,则使用轮询选出一个Partition。

 

日志存储:

Kafka是使用日志文件来保存每个Partition中的数据的,每条消息用offset来表示它在分区中的偏移量,这是个逻辑值,不代表消息存储的物理地址。同一个Partition中的消息是顺序写的,这样就避免了随机写入带来的不连续问题,写磁盘的时候也是顺序的,读取的时候也会很快。

Partition的数据是存储在Log日志中,当然为了避免日志文件过大,Log并不是一个文件,而是一个文件目录,目录的命名规则为:_。目录下面会有多个Log文件,对应会有多个索引文件。随着消息的不断写入,日志文件的大小达到一个阈值时,就会创建新的日志文件和索引文件继续写入新的数据。日志文件的命名格式为[baseOffset].log,baseOffset是该日志文件中第一条消息的offset。

Kafka基本概念整理_第2张图片

 

Partition副本:

同一个partition可能会有多个replication(对应的server.properties 配置中的default.replication.factor=N,也可以在创建Topic的时候手动指定,1表示没有副本)。没有repliction的情况下,一旦broker宕机,其上所有的数据都不可被消费,同时producer上也不能将数据存放在对应的partition。引入replication之后,同一个partition可能会有多个replication,而这时需要在这些replication上选出一个leader,producer和consumer只与这个leader交互,其他的replication作为follower主动从lear中复制数据。所以此处又引入了ISR的概念。

 

ISR(In-Sync Replica):

ISR表示目前可用的消息量与Leader差不多的副本集合。它有以下两个含义:

  1. 副本所在的broker必须保持与Zookeeper的连接
  2. 副本最后一条消息的offset和leader最后一条消息的offset之间的差不能超出指定的阈值

每个Partition的Leader会维护本Partition的ISR集合。写请求首先由Leader处理,之后Follower会从Leader上拉取数据,这个过程会有一定的延迟,所以Follower上的数据会比Leader偏少。如果由于网络延迟太大导致Leader和Follower之间相差太多,Follower会被踢出ISR集合。Follower恢复正常之后会继续与Leader进行同步,当差值再次小于阈值时,Follower会重新加入ISR中。流程图如下:

Kafka基本概念整理_第3张图片

1. producer先从zk的/broker/../state节点找到该partition的leader

2. producer将消息发送给该leader

3. leader将消息写入本地log

4. followers从leader pull消息,写入本地log后向leader发送ack

5. leader 收到所有的followers发送的ack后,向producer发送ack。

再次重申下,follewer 只是数据的副本提供数据的可恢复性,本身和kafka 的读写性能无关,Kafka的读写都只是和leader 相关

 

补充: HW&LEO

HW(High Watermark)与LEO(Log End Offset)和上面说到的ISR有密切的联系。

HW标记了一个offset,Consumer消费消息的时候只能拉取到HW之前的消息,它由Leader管理。当ISR集合中所有的Follower都拉取HW指定消息进行同步后,Leader会更新HW的值,称这些数据已经‘commit’了。此时即使Leader副本损坏,HW之前的数据也可以在Follower上找到。

LEO是指Leader和Follower最后一条记录的offset值。

 

 

五、Consumer消费Kafka的数据

在上面的“二”中说过,消费模式有两种,点对点(pull)还有发布订阅(push):

Push模式:有消息代理记录消费者的消费状态,消息代理在将消息推送到消费者后,标记这条消息为已消费,但这种方式无法很好的保证消息被处理。比如,消息代理把消息发送出去后,当消费进程挂掉或者由于网络原因没有收到这条消息时,可能就会造成消息丢失。

pull模式:由消费者自己记录消费状态(offset),每个消费者互相独立地顺序读取每个分区的消息,如下图所示,有两个消费者拉取同一个主题的消息,消费者A的消费进度是3,消费者的消费进度是6.消费者拉取得最大上限通过最高水位控制,生产者最新写入的消息如果还没有达到备份数量,对消费者是不可见的。这种有消费者控制偏移量的有点是消费者可以按照任意顺序消费信息,比如,消费者可以重置旧的偏移量,重新处理之前已经消费过的消息,或者直接跳到最近的位置,从当前的时刻开始消费。kafka中的消息不会随着消费者的消费而丢失,其丢失机制与设定的时间和大小有关。(本人都是用的这种)

Kafka基本概念整理_第4张图片

Push VS Pull 优缺点:

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

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

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

 

再说Consumer Group:

消费者是以consumer group消费者组的方式工作,由一个或者多个消费者组成一个组,共同消费一个topic。每个分区在同一时间只能由group中的一个消费者读取,但是多个group可以同时消费这个partition。消费者可以通过水平扩展的方式同时读取大量的消息。另外,如果一个消费者失败了,那么其他的group成员要载均衡读取之前失败的消费者读取的分区。

 

Consumer 高级API:

不需要自行去管理offset,系统通过zookeeper自行管理。

不需要管理分区、副本等情况,有系统自动管理。

消费者断线会自动根据上一次记录在zookeeper中的offset去接着获取数据。可以使用group来区分对同一个topic的不同程序访问分离开来。(不同的group记录不                      同的offset,这样不同程序读取同一个topic才不会因为offset互相影响)

 

Consumer 低级API:

能够让开发者自己控制offset,想从哪里都就从哪里读取。

自行控制连接分区,对分区自定义进行负载均衡

对zookeeper的依赖性降低(如offset不一定非要靠zk存储,自行存储offset即可,比如存放在文件或者内存中)

 

参考:

《Apache Kafka源码剖析》

你可能感兴趣的:(Kafka学习)