1. 简介
Kafka 是linkedin 公司用于日志处理的分布式消息队列,同时支持离线和在线日志处理。kafka 对消息保存时根据Topic进行归类,发送消息者成为Producer,消息接受者成为Consumer,此外kafka 集群有多个kafka 实例组成,每个实例(server)称为broker。无论是kafka集群,还是producer和consumer 都依赖于zookeeper 来保证系统可用性,为集群保存一些meta 信息。
Kafka 是一种分布式的、分区的、多副本的基于发布/订阅的消息系统。它是通过 zookeeper 进行协调,常见可以用于 web/nginx 日志、访问日志、消息服务等。主要应用场景为:日志收集系统和消息系统。
Kafka 的主要设计目标如下:
- 以时间复杂度为 O(1) 的方式提供持久化能力,即使对 TB 级别以上的数据也能保证常数时间的访问性能。
- 高吞吐率,即使在十分廉价的机器上也能实现单机支持每秒 100K 条消息的传输。
- 支持 Kafka Server (即 Kafka 集群的服务器)间的消息分区,及分布式消费,同时保证每个 partition 内的消息顺序传输。
- 同时支持离线数据处理和实时数据处理
2. Kafka 架构
一个 Kafka 集群由若干producer、若干consumer、若干broker,以及一个zookeeper集群所组成。
Kafka通过zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance。producer使用push模式将消息发布到broker,consumer使用pull模式从broker订阅并消费消息。
Kafka名词解释:
broker
:Kafka是由多个服务器组成的机器,每个服务器称作代理(broker)消息中间件处理结点,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群,相当于物理层面上的一台服务器。topic
:Kafka维护消息类别的东西是主题(topic)。存放同一类消息的位置,是一个概念层面上的名词,Kafka集群可以负责多个topic的分发。(物理上不同topic的消息分开存储,逻辑上一个topic的消息虽然保存于一个或多个broker上但用户只需指定消息的topic即可生产或消费数据而不必关心数据存于何处)partition
:topic在物理层面上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列,创建topic时可以指定partition数量,每个partition对应于一个文件夹,该文件夹下存储该partition的数据和索引文件。一般来说partition的数量大于等于broker的数量。producer
:负责发布消息到Kafka broker(生产者)consumer
:消费消息,每个consumer属于一个特定的consumer group(可为每个consumer指定group name,若不指定group name则为默认的group)。使用consumer high level API时,同一topic的一条消息只能被一个consumer group的一个consumer消费,但多个consumer group可同时消费这条消息。consumer group
:每个consumer属于一个特定的consumer group,consumer group是实际记录的概念。
3. 主题(Topics),日志(Logs)
一个Topic 可以认为是一类消息,每个topic 将被分成多个partition(区),每个partition 在存储层面是append log 文件。任何发布到此partition 的消息都会被直接追加到log 文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset 为一个long型数字,它是唯一标记一条消息。kafka 并没有提供其他额外的索引机制来存储offset,因为在kafka 中几乎不允许对消息进行“随机读写”。
每个分区就是一个提交日志:每个分区上保存着不断被追加的消息,这些消息是有序的且顺序不可改变;分区上的每个消息都被分配了一个序列号offset,offset唯一标识了分区上的消息。
在kafka 中,即使消息被消费,消息仍然不会被立即删除。日志文件将会根据broker 中的配置要求,保留一定的时间之后删除;比如log 文件保留2 天,那么两天后,文件会被清除,无论其中的消息是否被消费。kafka 通过这种简单的手段,来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘IO 开支。
对于consumer 而言,它需要保存消费消息的offset,对于offset的保存和使用, 由consumer 来控制; 当consumer 正常消费消息时,offset 将会"线性"的向前驱动,即消息将依次顺序被消费。事实上consumer 可以使用任意顺序消费消息,它只需要将offset 重置为任意值。(offset 将会保存在zookeeper 中,参见下文)
kafka集群几乎不需要维护任何consumer和producer 状态信息,这些信息由zookeeper 保存;因此producer和consumer 的客户端实现非常轻量级,它们可以随意离开,而不会对集群造成额外的影响。partitions的设计目的有多个。最根本原因是kafka基于文件存储。通过分区,可以将日志内容分散到多个server 上,来避免文件尺寸达到单机磁盘的上限,每个partiton都会被当前server(kafka实例)保存;可以将一个topic 切分多任意多个partitions来保存消息。此外越多的partitions 意味着可以容纳更多的consumer,有效提升并发消费的能力。(具体原理参见下文)。这些特性表明,Kafka的消费者是非常廉价的,一个消费者的创建、销毁不会对集群或其他消费者产生多大的影响。
对日志进行分区有几个目的:
- 1.扩容,一个主题可以有多个分区,这使得可以保存比一个机器保存的多的多的数据。
- 2.并行
4.分布式(Distribution)
一个Topic 的多个partitions,被分布在kafka 集群中的多个server 上;每个server(kafka 实例)负责partitions中消息的读写操作;此外kafka 还可以配置partitions 需要备份的个数(replicas),每个partition 将会被备份到多台机器上,以提高可用性。
基于replicated(冗余) 方案,那么就意味着需要对多个备份进行调度;每个partition 都有一个机器为"leader";零个或多个机器作为follower。leader 负责所有的读写操作,follower执行leader的指令。如果leader 失效,那么将会有其他follower 来接管(成为新的leader);follower只是单调的和leader 跟进,同步消息即可。由此可见作为leader 的server 承载了全部的请求压力,因此从集群的整体考虑,有多少个partitions就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定。
1.发送到partitions 中的消息将会按照它接收的顺序追加到日志中。
2.对于消费者而言,它们消费消息的顺序和日志中消息顺序一致。
3.如果Topic 的"replicationfactor"(备份因子)为N,那么允许N-1 个kafka实例失效。
5.生产者(Producers)
Producer 将消息发布到指定的Topic中,同时Producer 也能决定将此消息归属于哪个partition;这可以通过简单的循环的方式来实现,或者使用一些分区方法(比如根据消息的key来分区)
6.消费者(Consumers)
传统的消息传递有两种方式: 队列方式(queuing)、发布-订阅(publish-subscribe)方式.
队列方式:一组消费者从机器上读消息,每个消息只传递给这组消费者中的一个。
分布-订阅方式:消息被广播到所有的消费者。Kafka提供了一个消费组(consumer group)的说法来概括这两种方式。
消费者都属于一个消费组;反过来说,每个消费组中可以有多个消费者。发送到Topic的消息,只会被订阅此Topic的每个消费组中的一个消费组消费。如果所有的消费者都具有相同的消费组,这种情况和queue模式很像;消息将会在consumers之间负载均衡。如果所有的consumer 都具有不同的group,那这就是"发布-订阅",消息将会广播给所有的消费者。
在kafka 中,一个partition 中的消息只会被group 中的一个consumer 消费;每个group 中consumer 消息消费互相独立;我们可以认为一个group 是一个"订阅"者,一个Topic 中的每个partions,只会被一个"订阅者"中的一个consumer消费,不过一个consumer 可以消费多个partitions 中的消息。kafka只能保证一个partition中的消息被某个consumer 消费时,消息是顺序的。事实上,从Topic 角度来说,消息仍不是有序的。kafka 的设计原理决定,对于一个topic,同一个group 中不能有多于partitions 个数的consumer 同时消费,否则将意味着某些consumer 将无法得到消息。
1.Kafka将主题下的分区分配给消费组里的消费者,每个分区被一个消费者消费
2.消费者的数量不能超过分区数
3.Kafka只能保证分区内的消息是有序的
4.如果你想要消息是全局有序的,你可以设置主题只有一个分区,同时这意味着只能有一个消费者
7. Kafka数据传输的事务特点
- at most once
这种模式下consumer fetch消息,先进行commit,再进行处理。如果再处理消息的过程中出现异常,下次重新开始工作就无法读到之前已经确认而未处理的消息。
- at most once
- at least once
消息至少发送一次,如果消息未能接受成功,可能会重发,直到接收成功。消费者fetch消息,然后处理消息,然后保存offset。如果消息处理成功之后,但是在保存offset阶段zookeeper异常导致保存操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息,这就是”at least once”,原因offset没有及时的提交给zookeeper,zookeeper恢复正常还是之前offset状态。
- at least once
- exactly once
消息只会发送一次,Kafka中并没有严格的去实现,我们认为这种策略在Kafka中是没有必要的。
通常情况下,Kafka默认保证at least once。
- exactly once
- Push & Pull
作为一个消息系统,Kafka遵循了传统的方式,选择由producer向broker push消息,并由consumer从broker中pull消息。
push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。push模式的目标就是以尽可能快的速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现是拒绝服务以及网络拥塞。而pull模式可以根据consumer的消费能力以适当的速率消费消息。
- Push & Pull
- Topic & Partition
Topic在逻辑上可以认为是一个存在的queue,每条消息都必须指定它的topic,可以简单的理解为必须指明把这条消息放进哪个queue里。为了使Kafka的吞吐率可以水平扩展,物理上把topic分成一个或多个partition,每个partition在物理上对应一个文件夹,该文件夹下存储这个partition的所有消息和索引文件。
- Topic & Partition