Kafka设计及其原理
Kafka简介
Apache Kafka是一个分布式的基于push-subscribe的流平台。
一个流平台有3个关键的功能:
- 发布和订阅流记录,类似于消息队列或者企业级消息系统
- 以容错的持久方式记录流
- 处理刚发生的流记录
Kafka通常被用于两大类应用:
- 构建可以在系统和应用程序之间可靠的获取实时的流数据管道
- 构建对流记录进行转换并作出响应的实时流应用程序
Kafka部分概念:
- Kafka以集群的方式运行在一个或多个跨数据中心的服务器上
- Kafka集群以称为
topic
的类别来存储流记录 - 每一条流记录由一个key、一个value和一条时间戳构成
Kafka有4个核心的API:
- Producer API : 允许应由程序推送流记录到一个或多个
Kafka topics
- Consumer API : 允许应有程序订阅一个或多个
Kafka topics
并处理生成的流记录 - Streams API : 允许应用程序扮演流处理器,消耗来自一个或多个主题的输入流并生成到一个或多个输出主题的输出流,从而有效地将输入流转换为输出流。
- Connector API : 允许构建和运行将
Kafka topics
连接到现有应用程序或数据系统的可重用生产者或使用者。 例如,关系数据库的连接器可能捕获对表的每个更改。
Kafka技术概览
Kafka特性
- 高吞吐量、低延迟:Kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒。
- 可扩展性:Kafka集群支持热扩展。
- 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据热备份防止丢失。
- 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)。
- 高并发:支持数千个客户端同时读写。
Kafka部分重要的设计思想
- Consumer Group:各个consumer可以组成一个组,每条消息只能被组中的一个consumer消费,如果一个消息要被多个consumer消费,那么必须满足这些consumer不在同一个消费组。
- 消息状态:在Kafka中,消息的状态被保存在consumer中,broker不会关心哪条消息被消费且被谁消费,只记录一个offset值(指向partition中下一个要被消费的消息位置),这就意味着consumer如果处理不好的话,broker的上一个消息可能会被多次消费。
- 消息持久化:Kafka中会把消息持久化到本地文件系统中,并且保持极高的效率。
- 消息有效期:Kafka会长久保留其中的消息,以便consumer可以多次消费,该有效期可以配置。
- 批量发送:Kafka支持以消息集合为单位进行批量发送,以提高push效率。
- push-and-pull:Kafka中的Producer和Consumer采用的是push-and-pull模式,即Producer只管向broker push消息,Consumer只管向broker pull消息。两者对消息的生产和消费是异步的。
- 同步异步:Producer采用异步push方式,极大提高消息的吞吐量(可以通过参数来控制同步或者异步)
- Kafka集群中broker之间的关系:集群中各broker之间相互对等,无主从关系。
Kafka架构组件
Kafka中发布订阅的对象是topic
,我们可以为每一类数据创建一个topic
,把向topic
发布消息的客户端称为Producer
,把向topic
消费消息的客户端称为Consumer
。Producers
和Consumers
可以同时向多个topic进行读和写。一个Kafka集群有一个或多个Broker
服务器组成,它负责持久化和备份具体的Kafka消息。
- topic:消息存放的目录即主题
- Producer:生产消息到topic的一方
- Consumer:消费消息从topic的一方
- Broker:Kafka的服务实例
Topics and Logs
一个topic
就是所发布的记录的一个种类或者流名称。Topics
在Kafka中总是被多订阅。换句话说,一个topic
可以有0个、1个或者多个消费者订阅写入它的数据。
对于每个topic
,Kafka集群维护着一个如下图的分区日志:
每个分区都是一个有序的、不可变的记录序列,不断的被附加到结构化的提交日志中。分区中的每条记录都被分配了一个称为offset
的序列ID,它被用来作为在分区中每条记录的唯一标识。
Kafka集群可以永久的持久化所发布的记录,不论该记录是否被消费,也可以通过参数配置记录所保留的时间。比如,如果保留策略被设置为两天,那么在记录被发布后的两天内,该消息是可以被消费的,但超过两天之后,这条消息将会被废弃以释放空间。Kafka的性能在数据大小方面是恒定的,因此长时间存放数据是没有问题的。
Kafka需要维持的元数据只有一个,即消费消息在Partition中的offset值,Consumer每消费一个消息,offset就会加1。其实消息的状态完全是由Consumer控制的,Consumer可以跟踪和重设这个offset值,这样的话Consumer就可以读取任意位置的消息。
把消息日志以Partition的形式存放有多重考虑,第一,方便在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了;第二就是可以提高并发,因为可以以Partition为单位读写了。
Kafka核心组件
-
Replications:Kafka允许用户为每个topic设置副本数量,副本数量决定了几个broker来存放写入的数据。如果你的副本数量设置为3,那么一份数据就会被保存到3台不同的机器上,那么就允许两台机器失败。一般推荐副本数量至少为2,这样就可以保证增减、重启机器时不会影响到数据消费。如果对持久化有更高的要求,那么可以把备份数量设置为3或者更多。
-
Partitions:Kafka中的topic是以分区的形式存放的,每一个topic都可以设置它的partition数量,partition的数量决定了组成topic的log数量。副本也都是以partition为单位。在多个副本中,只有一个分布会被选举为leader来作为读写用。在设置partition的数量时,需要考虑到一个partition只能被一个consumer消费(一个consumer可以同时消费多个partition),所以partition数量需要结合实际中consumer的数量来进行设定,如果partition的数量小于consumer的数量,那么就会存在有的consumer消费不到数据,推荐partition的数量大于或等于同时运行的数量,与此同时,partition的数量建议大于集群中broker的数量,这样leader partition可以均匀的分布在各个broker中,最终是的集群负载均衡。
-
Producer:Producers直接发送消息到broker上的leader partition,不需要经过任何中介一系列的路由转发。为了实现这个特性,kafka集群中的每个broker都可以响应producer的请求,并返回topic的一些元信息,这些元信息包括哪些机器是存活的,topic的leader partition都在哪,现阶段哪些leader partition是可以直接被访问的。Producer客户端自己控制着消息被推送到哪些partition。实现的方式可以是随机分配、实现一类随机负载均衡算法,或者指定一些分区算法。Kafka提供了接口供用户实现自定义的分区,用户可以为每个消息指定一个partitionKey,通过这个key来实现一些hash分区算法。比如,把userid作为partitionkey的话,相同userid的消息将会被推送到同一个分区。 以Batch的方式推送数据可以极大的提高处理效率,kafka Producer 可以将消息在内存中累计到一定数量后作为一个batch发送请求。Batch的数量大小可以通过Producer的参数控制,参数值可以设置为累计的消息的数量(如500条)、累计的时间间隔(如100ms)或者累计的数据大小(64KB)。通过增加batch的大小,可以减少网络请求和磁盘IO的次数,当然具体参数设置需要在效率和时效性方面做一个权衡。 Producers可以异步的并行的向kafka发送消息,但是通常producer在发送完消息之后会得到一个future响应,返回的是offset值或者发送过程中遇到的错误。这其中有个非常重要的参数
acks
,这个参数决定了producer要求leader partition 收到确认的副本个数,如果acks设置数量为0,表示producer不会等待broker的响应,所以,producer无法知道消息是否发送成功,这样有可能会导致数据丢失,但同时,acks值为0会得到最大的系统吞吐量。 若acks设置为1,表示producer会在leader partition收到消息时得到broker的一个确认,这样会有更好的可靠性,因为客户端会等待直到broker确认收到消息。若设置为-1,producer会在所有备份的partition收到消息时得到broker的确认,这个设置可以得到最高的可靠性保证。 Kafka 消息有一个定长的header和变长的字节数组组成。因为kafka消息支持字节数组,也就使得kafka可以支持任何用户自定义的序列号格式或者其它已有的格式如ApacheAvro、protobuf等。Kafka没有限定单个消息的大小,但我们推荐消息大小不要超过1MB,通常一般消息大小都在1~10kB之前。 -
Consumer:Kafka提供了两套consumer api,分为high-level api和sample-api。Sample-api 是一个底层的API,它维持了一个和单一broker的连接,并且这个API是完全无状态的,每次请求都需要指定offset值,因此,这套API也是最灵活的。 在kafka中,当前读到消息的offset值是由consumer来维护的,因此,consumer可以自己决定如何读取kafka中的数据。比如,consumer可以通过重设offset值来重新消费已消费过的数据。不管有没有被消费,kafka会保存数据一段时间,这个时间周期是可配置的,只有到了过期时间,kafka才会删除这些数据。High-level API封装了对集群中一系列broker的访问,可以透明的消费一个topic。它自己维持了已消费消息的状态,即每次消费的都是下一个消息。High-level API还支持以组的形式消费topic,如果consumers有同一个组名,那么kafka就相当于一个队列消息服务,而各个consumer均衡的消费相应partition中的数据。若consumers有不同的组名,那么此时kafka就相当与一个广播服务,会把topic中的所有消息广播到每个consumer。
参考: Heaven-Wang