Kafka入门介绍

561 阅读11分钟

最近正在学习Kafka,按照自己的理解整理一些关于Kafka的入门知识,并且希望对各位同学有所帮助。

Kafka简介

Kafka是最初由Linkedin公司开发,是一个基于zookeeper协调的分布式的提交日志系统(a distributed commit log),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

kafka既是消息引擎系统,也是分布式流数据处理平台。

Kafka主要设计目标如下:

  • 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间的访问性能。
  • 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输。
  • 支持Kafka Server间的消息分区,及分布式消费,同时保证每个partition内的消息顺序传输。
  • 同时支持离线数据处理和实时数据处理。
  • Scale out:支持在线水平扩展

kafka架构

Kafka拓扑结构

kafka架构.svg

如上图所示,一个kafka集群由多个Broker和一个Zookeeper集群组成,并且Kafka 支持水平扩展,一般 broker 数量越多,集群吞吐率越高。Kafka 通过 Zookeeper 管理集群配置,选举 leader,以及在 Consumer Group 发生变化时进行 rebalance。可以由多个Producer往kafka里生产数据,由多个Consumer Group从kafka里消费数据,Producer 使用 push 模式将消息发布到 broker,Consumer 使用 pull 模式从 broker 订阅并消费消息。

这里关于kafka的术语很多,下面简单介绍一下kafka的基本术语。

Kafka术语

  • Broker

    • Kafka 集群包含一个或多个服务器,服务器节点称为broker。
    • broker存储topic的数据。如果某topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。
    • 如果某topic有N个partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个broker不存储该topic的partition数据。
    • 如果某topic有N个partition,集群中broker数目少于N个,那么一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致Kafka集群数据不均衡。
  • Topic

    • 每条发布到 Kafka 集群的消息都有一个类别,这个类别被称为 Topic。
    • 类似于数据库的表名。
  • Partition

    • 每个 Topic 包含一个或多个 Partition。
    • 每个partition中的数据使用多个segment文件存储。
  • Producer

    • 数据的发布者,该角色将消息发布到Kafka的topic中。
    • 生产者发送的消息,存储到一个partition中,生产者也可以指定数据存储的partition。
  • Consumer Group

    • 每个 Consumer 属于一个特定的 Consumer Group。
    • 可为每个 Consumer 指定 group name,若不指定 group name 则属于默认的 group
  • Consumer

    • 消费者可以从broker中读取数据。
    • 消费者可以消费多个topic中的数据。
  • Replica

    • Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的Replica。
    • 分为Leader Replica和Follower Replica。

关系解释

我们已经基本了解kafka相关术语的解释,但这些组件之间的关系是什么样的呢?

Topic&Partition&Replica

Topic在逻辑上相当于是一个Message Queue,每条消息都必须指定一个Queue来存储。为了提高Kafka的吞吐率,Topic在物理上会分为多个Partition,每个Partition会在对应的Broker所在机器上有一个文件夹,这个文件夹在存放着该Partition上的message和index。如果Kafka集群里有2个Topic,Topic 1有3个Partition,Topic 2有5个Partition,那么在整个Kafka集群里会创建8个文件夹来存储。

Partition的消息会Append到log文件的尾部,顺序写入磁盘(这也是高吞吐率的原因之一)。每条消息在log文件中的位置成为offset(偏移量),offset为一个long型数字,唯一标记一条消息。

kafka-write-partition.png

log文件里会存在哪些内容(消息格式):

message length : 4 bytes (value: 1+4+n)
"magic" value : 1 byte 
crc : 4 bytes 
payload : n bytes 
offset :unique long

虽说数据存储在各个Partition里,不会受限于一台机器,可以分布在多台机器上,但是如果数据过大的时候还是会影响读写性能。所以Kafka会将log文件根据文件大小,时间等策略分成多个segment文件,每个segment文件都会有索引,可以通过索引来提高读写性能。

对于传统的 message queue 而言,一般会删除已经被消费的消息,而 Kafka 集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此 Kafka 提供两种策略删除旧数据。一是基于时间,二是基于 Partition 文件大小。例如可以通过配置 $KAFKA_HOME/config/server.properties,让 Kafka 删除一周前的数据,也可在 Partition 文件超过 1GB 时删除旧数据,配置如下所示。

# The minimum age of a log file to be eligible for deletion
log.retention.hours=168
# The maximum size of a log segment file. When this size is reached a new log segment will be created.
log.segment.bytes=1073741824
# The interval at which log segments are checked to see if they can be deleted according to the retention policies
log.retention.check.interval.ms=300000
# If log.cleaner.enable=true is set the cleaner will be enabled and individual logs can then be marked for log compaction.
log.cleaner.enable=false

kafka是号称高可以的分布式消息系统,但是如果某个Partition所在的Broker挂了导致Partition失效了,导致数据丢失,那还怎么保证高可用呢?一般来说,分布式存储系统保证高可用的手段通常是做数据冗余(Replica),同样Kafka也是这么设计的,Kafka会通过选举策略来选出某个Partition的Leader Replica和Followe Replica。

例如某个 Topic 分成了 3 个 Partition,每个 Partition 保存了两个副本,副本平均分配到 3 个 Broker 上。即使有一个 Broker 挂了,剩余的两个 Broker 依旧能正常工作,如果有Broker挂了,kafka会重新进行选举。

此外还需要知道的是,对kafka的读写请求都会通过Leader Replica来处理,数据的同步,也是把Follower Replica当成一个Consumer来从Leader Replica处同步消息。

Producer

主要工作是向Topic生产消息。Topic有多个Partition组成(通过num.partitions参数指定),那生产的消息是知道需要路由到哪个Partition呢?这个主要由分区策略决定的

一般的分区策略有:

  1. 轮询
  2. 随机
  3. Hash

也可以自定义分区策略,实现Partitioner接口

主要工作是向Topic生产消息。但由于副本机制,生产者发送消息怎么样算发送成功呢?在一般的分布式系统中一般采用过半提交的方式确保,既一半以上的副本确认成功才算消息提交成功,但Kafka中并未提供这种机制。 可以通过以下参数配置保障:

Producer提供配置acks参数

  • acks=0 生产者只要将消息发送出去,无需等待任何副本的确认,即算发送成功。此方式吞吐量最大、性能最好,但kafka服务抖动时容易丢消息
  • acks=1 默认。生产者将消息发送出去,只需副本中的leader确认,即算发送成功。此方式吞吐量和性能优秀,但当leader挂时会造成小部分消息丢失
  • acks=all 生产者将消息发送出去,需要ISR中的副本全部确认,即算发送成功。此方式吞吐量和性能差,但稳定性最高,消息不容易丢失

这个里有个新的术语,什么是ISR?除了ISR还有AR和OSR也是需要清楚的。

  • AR(Assigned Replicas):分区中的所有副本统称为AR。
  • ISR(In-Sync Replicas):所有与leader副本保持一定程度同步的副本(包括leader副本在内)组成ISR,ISR集合是AR集合中的一个子集。
  • OSR(Out-of-Sync Replicas):与leader副本同步滞后过多的副本(不包括leader副本)组成OSR

leader副本负责维护和跟踪ISR集合中所有follower副本的滞后状态,当follower副本落后太多或失效时(replica.lag.time.max.msBroker提供的配置参数),leader副本会把它从ISR集合中剔除。

如果OSR集合中有follower副本“追上”了leader副本,那么leader副本会把它从OSR集合转移至ISR集合。

默认情况下,当leader副本发生故障时,只有在ISR集合中的副本才有资格被选举为新的leader,而在OSR集合中的副本则没有任何机会。

Consumer & Consumer Group

主要工作是从Topic消费消息,既然主要工作是消费消息,就会有这样一些问题,比如它的消费模式是什么?是点对点模式还是发布订阅模式?是Topic Push消息给Consumer还是Consumer主动从Topic里Pull消息?Topic是会被分成若干个Partition,Consumer消费的是哪个Partition里的数据?是怎么分配的?

接下来我们就对着这些问题,一一说道说道。

消费模式

传统的消息引擎系统主要有两种主要的消息传递模式:

  • 点对点模式:

    • 多个消费者共同消费同一个队列,每条消息只发送给一个消费者,可以快速地消费掉队列里的消息。

point-to-point-queue.svg

  • 发布-订阅模式:

    • 多个消费者订阅主题,每个消息会发布给所有的消费者。
    • 一个消息可以被多次消费,能支持冗余的消费。

publish-subscribe.svg

点对点模式的主要优点就是可以多个消费者共同消费同一个队列,效率高,而发布订阅模式的优点则是一个消息可以被多次消费,能支持冗余的消费。为了两种模式的优点都能够用上,来提高消费的效率和伸缩性,kafka引入了Consumer Group的概念。

  • Consumer Group 是以发布/订阅模式工作的。
  • Consumer Group里的Consumer是以点对点模式工作的。

mixer.svg

Pull VS Push

  • push: 当生产者发送消息的速度比消费者消费消息的速度快时,Broker服务一直向消费者push消息,消费者可能承受不了压力而宕机。但这样的好处是,Broker不容易积压消息。
  • pull: 当生产者发送消息的速度比消费者消费消息的速度快时,消费者只是消费落后,后面可能赶上。消息积压在Broker上。

Kafka Consumer采用的是pull模式,Consumer可以自由的控制消费的速率。

Consumer & Consumer Group & Partition

Topic由多个Partition组成,Producer往Toplic里生产消息,这些消息会通过分区策略分配到不同的Partition,那Consumer Group定于的Topic能按照生产消息的顺序消费吗?

答案是不能的,Kafka 只会保证在 Partition 内消息是有序的,而不管全局的情况。

同一 Topic 的一条消息只能被同一个 Consumer Group 内的一个 Consumer 消费,但多个 Consumer Group 可同时消费这一消息。

那Consumer是如何选择Partition进行消费的呢?默认情况下(RangeAssignor)是:

  1. 若consumer group中的consumer数量少于partition数量,则至少有1个consumer会消费多个partition数据
  2. 若consumer group中的consumer数量多于partition数量,则会有部分consumer无法消费该topic中任何一条消息
  3. 若consumer group中的consumer数量等于partition数量,则正好一个consumer消费一个partition数据

为了保证Consumer负载大致相同,Kafka除了Range还提供了另外两种算法:RoundRobinSticky

小结

  • Topic是一个抽象概念,相当于一个Message Queue,但是Partition才是消息存储的实际单位。Producer生产的消息实际上是根据一些分区策略存储到Partition里,Consumer也会通过分区分配策略来从Partition里消费数据。
  • Kafka 会保证一个 Consumer 收到的消息中,来自同一个 Partition 的所有消息是有序的。而来自不同 Partition 的消息则不保证有序。
  • Kafka有Consumer Group的概念,用于将点对点模式和发布订阅模式相结合。Consumer Group对应着发布订阅模式,即多个Group可以订阅相同的Topic,且互不干扰。Consumer Group里Consumer实例对应着点对点模式,顺序消费消息,并且效率高。
  • 消息是存在 Broker 上的,一般对应为一台物理机或集群。存储时,每个 Partition 都可以有多个副本。
  • 对于一个 Partition,它的多个复本存储一般存储在不同 Broker 中,在同一时刻会由 Zookeeper 选出一个主副本来负责所有的读写操作。