基本概念
Kafka是一个分布式、可分区、可复制的消息系统,Kafka系统设计的术语整理如下:
Topic: Kafka消息以Topic为单位进行管理,Topic为业务逻辑单元。
Producer: 消息发送者,是向Topic发生消息的程序主体。
Consumer: 消息消费者:是从Topic中获取消息进行消费的主体。
Broker: Kafka以集群方式运行,每个服务进程叫Broker。
Partition: 每个Topic中可包含一个或多个分区(Partition),每个分区都有一系列有序的、不可变的消息队列组成,Partition是确保消息有序消费的最小单元。
Consumer Group: 多个Consumer可以组成一个Group,通过Group实现消费者的集群和负责均衡;对于同一条消息,只会被Group中的一个Consumer消费。
Offset:消息队列中每条消息有一个连续的序号叫Offset,作为队列中消息的标识
Kafka系统消息队列数据生成和消费模型如下。
每个Producer和Consumer分别表示一个线程。
- 一个或多个Producer可向一个Topic写入数据
- 多Partition的Topic,Producer数据写入到Partition
- 多个Consumer可以消费同一个Topic(Partition),但一个Consumer不可消费多个Topic
- 多个Consumer可以组成Consumer Group,以负载分担形式消费数据
和一般的消息队列组件不同,Kafka可在配置的时间段内,保留消息队列中的所有消息,不管消息是否已经被消费。比如配置的消息保留时间为24小时,则在24小时内,消息可以被重复消费。由于这个特性,Consumer每消费一条消息时,需要记录当前消息的Offset并持久化,以便在Consumer重启后,知道从什么地方开始消费。当然Consumer启动后,可以从头、从任意位置、从最新消息等任意方式开始消费消息。
Patition机制
多副本
Kafka作为一个分布式消息系统,为了提高容错能力,每个Patition可以设置若干副本;副本分布在不同的Broker中,其中一个副本作为Leader,负责处理消息的写入和消费任务,其他副本为Follower,负责同步Leader的数据;当Leader节点异常时,其中一个Follower升级为Leader。集群中的每个Broker,运行一部分Patition的Leader,同时作为其他Patition的Follower,为集群提供负责均衡能力。
消息和Patition映射
当Topic支持多Patition模式时,Producer写入消息需要选择写入哪个Patition,Producer可以选择随机选择Patition,实现负载分担。由于Patition是消息有序的最小单元,更多情况是根据消息中的某个业务Key字段,作为分区选择函数的输入,达到相同业务Key的数据写入同一个Patition的目的。
消息消费模式
Kafka提供了两种消息的消费模式,即消息队列Queuing和发布订阅Publish-Subscribe模式。
- 消息队列模式:多个Consumer可以同时从Topic读取消息,但每个消息只能被一个Consumer读到。
- 发布订阅模式:Topic发布的的消息,会广播到所有Consumer。
Kafka可以把多个Consumer组成Consumer Group,Group中只有一个Consumer可以读到Topic的消息,这样可以实现Consumer的负载均衡。
如果把所有Consumer分散到不同的Group,则每个Consumer都可以读到Topic的消息,这就成了发布订阅模式;如果把所有Consumer加入同一个Group,则只有一个Consumer可以读到消息,成了消息队列模式。对于容错和稳定性高的系统,可以设计为由多个Consumer Group订阅一个Topic。
有序性 VS 负载均衡
Kafka通过Partition的概念,可以在多个Consumer Group并发的情况下提供较好的有序性和负载均衡。将每个Patition分只分发给一个Group,因为Patition数据只被Group里的一个Consumer消费,达到了消息顺序消费的目的。同时,因为有多个Patition,依然可以在多个Group之间进行负载均衡。
Producer流程
Partition机制
在多Partition的Topic中,在不指定Partition Key的情况下,Kafka采用负载均衡的机制随机分配Partition;用户也可以指定分区的Key,系统根据hash(key) % partition_count
函数,把数据hash到不通的Partition,用户也可以覆盖分区函数实现自己的hash逻辑。
数据分区的方式,要根据业务逻辑来设置。例如要保证同一个用户消息处理的有序性,就应该使用用户ID进行hash。
消息发送
Kafka允许进行批量异步发送消息,有效的提高发送效率。客户端先将消息批量缓存在本地内存中,然后一次性发送到服务器。具体的缓存策略,可以配置为根据数据条数或缓存时间(如:每满20条发送一次或每5s发送一次)。
由于消息缓存是在Producer客户端进行的,如果客户端崩溃,会导致缓存中未发送的消息丢失,最新版本的Kafka提供了回调机制,每条消息被服务器接纳后,会通知Producer客户端,Producer需要通过此回调机制来确认数据是否被提交。
主从同步
Kafka中每个Partition会有多个副本(一个Leader和多个Follower)和高可用(Leader异常后Follower升级为Leader接管服务)机制。
Kafka中保证多副本数据同步的机制和ZAB或Raft协议的投票、过半机制有所不同。
Kafka通过Zookeeper维护着一个Partition的同步状态(in-sync)节点(Leader + Follower)集合(a set of in-sync replicas,简称:ISR)。当Producer提交数据时,只有这条数据被所有ISR同步并写入日志后,消息状态才是Committed
的,只有Committed的消息才会回调通知Producer和发送给Consumer。通过这个机制,如果Leader异常了,ISR中所有节点都可以承接Leader的工作。
ISR集合是通过动态维护的,集合中的节点必须满足如下条件:
- 节点注册到Zookeeper中,Zookeeper通过心跳机制检查到节点是活动状态
- 集合中的Follower节点,必须能及时同步Leader节点的消息(是否及时,主要通过消息落后的条数和延迟时间来判断)
当节点不符合条件时,会被提出ISR集合;恢复后又被加入。
Consumer流程
对于消息队列,当有新消息产生时,有两种方式来触发客户端消费消息:一种是服务端主动通知,即Push;另一种是客户端轮询,即Pull。
Push Or Pull
采用Push的优点时可以避免服务端处理轮询的工作量,并提供消息消费的及时性。缺点是如果消息量大时,一条一条的推送,会降低数据推送的效率,更极端的情况,如果数据量大于Consumer处理能力时,会导致Consumer资源耗尽甚至崩溃。
采用Pull模式的优点,Consumer可以通过制定Offset从任意位置开始消费;Consumer可以通过批量拉取数据方式提高网络利用率;可以根据Consumer的处理能力,量力而行拉取数据进行消费。缺点是如果数据量小时,空轮询浪费资源。
Kaka采用了Pull的方式来消费消息,并且通过没有消息是让Consumer阻塞来避免空轮询的消耗。
Offset
Kafka的消息消费状态的跟踪记录,和大部分消息队列系统通过服务端来跟踪的模式不同,采用客户端跟踪的模式,这样设计也能大大简化消息消费时服务端和客户端交互的设计。
Kafka可以通过offset标记从任意位置开始消费消息,服务端并不记录消息的消费状态,而是在Consumer消费每条消息后,通过Zookeeper保存offset来跟踪消费状态。enable.auto.commit
参数用于控制Consumer消息消费后是否自动提交offset,建议把改值设置为false,由程序处理完消息的业务逻辑后手工commit offset。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。