kafka(一)—基本概念

本文已同步至个人博客liaosi'blog-kafka(一)—基本概念

Kafka简介

Kafka是用scala语言编写,最初由Linkedin公司开发,后贡献给了Apache基金会并成为顶级开源项目。是一个分布式、支持分区的(partition)、多副本的(replication),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等。

Kafka是一个类JMS消息队列,结合了JMS中的两种模式,可以有多个消费者主动拉取数据。虽然它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现,在JMS中只有点对点模式才有消费者主动拉取数据。

1.Kafka的特性

  • 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition,consumer group 对 partition 进行consume操作。
  • 可扩展性:kafka集群支持热扩展
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
  • 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
  • 高并发:支持数千个客户端同时读写
  • 顺序性:由生产者发送到一个特定的主题分区的消息将被以他们被发送的顺序来追加。也就是说,
    如果一个消息M1和消息M2都来自同一个生产者,M1先发,那么M1将有一个低于M2的偏移,会更早在日志中出现。
    消费者看到的记录排序就是记录被存储在日志中的顺序。

2.Kafka的使用场景:

  • 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如Hadoop、Hbase、Solr等。
  • 消息系统:解耦生产者和消费者、缓存消息等。
  • 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
  • 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
  • 流式处理:比如 Spark Streaming 和 Storm。
  • 事件源

Kafka的几个重要概念

官方文档:Kafka官网文档
Kafka的运行架构如下图,各组件之间通过TCP协议通信:

1.Broker

kafka 集群由多个 kafka 实例组成,每个实例 (server) 称为 broker ,在集群中每个broker都有一个唯一brokerid,不得重复。 无论是 kafka 集群,还是 producer 和 consumer 都依赖于 zookeeper 来保证系统可用性,为集群保存一些 meta (元数据)信息。

2.Topics And Logs

主题(topic)是一种分类或发布的一系列记录的名义上的名字。Kafka的主题始终是支持多用户订阅的;也就是说,一个主题可以有零个,一个或多个消费者订阅写入的数据。
对于每一个主题,Kafka集群保持一个分区日志文件,看下图:

每个分区都由一系列有序的、不可变的消息组成,这些消息被连续的追加到分区中。分区中的每个消息都有一个连续的序列号叫做offset,用来在分区中唯一的标识这个消息。

在一个可配置的时间段内,Kafka集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为2天,那么在一个消息被发布的两天时间内,它都是可以被消费的,之后它将被丢弃以释放空间。Kafka的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。

实际上每个consumer唯一需要维护的数据是消息在日志中的位置,也就是offset。这个offset有consumer来维护:一般情况下随着consumer不断的读取消息,这offset的值不断线性增加,但其实consumer可以在任何它想要的位置读取记录,比如它可以将offset设置成为一个旧的值来重读之前的消息。

正因为以上的特点,使Kafka consumers非常的轻量级:它们可以在不对集群和其他consumer造成影响的情况下读取消息。例如,你可以使用命令行工具去"tail"任何主题的内容,而不会对其他正在消费该消息的consumer造成影响。

4.Producers

Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,也可以根据一些更复杂的语义分区算法(比如基于记录一些键值)来完成。使用的更多的是第二种。默认是defaultPartition Utils.abs(key.hashCode) % numPartitions

5.Consumers

每个 Consumer 进程都会划归到一个逻辑的Consumer Group中,逻辑的订阅者是Consumer Group,同一个 Consumer Group 中的 Consumer 可以在不同的程序中,也可以在不同的机器上。所以一条message可以被多个订阅该 message 所在的topic的每一个Consumer Group 所消费,也就好像是这条message被广播到每个Consumer Group一样。而每个Consumer Group中,类似于一个Queue(JMS中的Queue)的概念差不多,即topic中的一条message只会被Consumer Group中的一个Consumer消费。

上图显示,一个kafka cluster中的某个topic有4个分区(P0-P3)和2个consumer组。A组有2个consumer,B组有4个consumer。

其实上面所说的订阅关系还不够明确,其实topic中的partition被分配到某个consumer上,也就是某个consumer订阅了某个partition。Consumer Group 订阅的是topic,但是consumer订阅的是partition,而不是message。所以在同一时间点上,订阅到同一个partition的consumer必然属于不同的Consumer Group。另外,partition分配的工作是在consumer leader中完成的。

Consumer 与 Consumer Group 的动态维护

当一个Consumer 进程挂掉 或者是卡住时,该Consumer所订阅的partition会被重新分配到该group内的其它的Consumer上。当一个consumer加入到一个Consumer Group中时,同样会从其它的Consumer中分配出一个或者多个partition 到这个新加入的Consumer。

当启动一个Consumer时,会指定它要加入的group,使用的是配置项:group.id。

为了维持Consumer 与 Consumer Group的关系,需要Consumer周期性的发送heartbeat到coordinator(协调者,在早期版本,以zookeeper作为协调者。后期版本则以某个broker作为协调者)。当Consumer由于某种原因不能发Heartbeat到coordinator时,并且时间超过session.timeout.ms时,就会认为该consumer已退出,它所订阅的partition会分配到同一group 内的其它的consumer上。而这个过程,被称为rebalance。

如果一个consumer 进程一直在周期性的发送heartbeat,但是它就是不消费消息,这种状态称为livelock状态。

Coordinator

Coordinator 协调者,协调consumer、broker。早期版本中Coordinator,使用zookeeper实现,但是这样做,rebalance的负担太重。为了解决scalable的问题,不再使用zookeeper,而是让每个broker来负责一些group的管理,这样consumer就完全不再依赖zookeeper了。

Consumer连接到coordinator

从Consumer的实现来看,在执行poll或者是join group之前,都要保证已连接到Coordinator。连接到coordinator的过程是:

1)连接到最后一次连接的broker(如果是刚启动的consumer,则要根据配置中的borker)。它会响应一个包含coordinator信息(host, port等)的response。

2)连接到coordinator。

Consumer Group Management

Consumer Group 管理中,也是需要coordinator的参与。一个Consumer要join到一个group中,或者一个consumer退出时,都要进行rebalance。进行rebalance的流程是:

1)会给一个coordinator发起Join请求(请求中要包括自己的一些元数据,例如自己感兴趣的topics)

2)Coordinator 根据这些consumer的join请求,选择出一个leader,并通知给各个consumer。这里的leader是consumer group 内的leader,是由某个consumer担任,不要与partition的leader混淆。

3)Consumer leader 根据这些consumer的metadata,重新为每个consumer member重新分配partition。分配完毕通过coordinator把最新分配情况同步给每个consumer。

4)Consumer拿到最新的分配后,继续工作。

Kafka如何保证消息的有序性

传统的队列在服务器上保存有序的消息,如果多个consumers同时从这个服务器消费消息,服务器就会以消息存储的顺序向consumer分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各consumer上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用“专用consumer”的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。

在这方面Kafka做的更好,通过分区的概念,Kafka可以在多个consumer组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个consumer组,这样一个分区就只被这个组的一个consumer消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个consumer组之间进行负载均衡。注意consumer组的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。

Kafka只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要topic中所有消息的有序性,那就只能让这个topic只有一个分区,当然也就只有一个consumer组消费它。

6.Distribution

Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。使用的更多的是第二种。默认是defaultPartition Utils.abs(key.hashCode) % numPartitions
每个分区(partition)在 Kafka 集群的若干服务中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的(replication-factor 1),副本使Kafka具备了容错能力。
每一个分区都由一个服务器作为“leader”,零或若干服务器作为“followers”,leader负责处理消息的读和写,与此同时,follower会被动的去复制leader上的数据。
如果leader发生故障,followers中的一台则会自动成为leader。每台服务器可以作为一些分区的leader,同时也可以作为其他分区的follower,这样集群就会据有较好的负载均衡。特别强调,和mysql中主从有区别,mysql做主从是为了读写分离,在kafka中读写操作都是leader。

在Kafka partition中,每个消息有一个唯一标识,即partition内的offset。每个consumer group中的订阅到某个partition的consumer在从partition中读取数据时,是依次读取的。

上图中,Consumer A、B分属于不用的Consumer Group。Consumer B读取到offset =11,Consumer A读取到offset=9 。这个值表示Consumer Group中的某个Consumer 在下次读取该partition时会从哪个offset的 message开始读取,即 Consumer Group A 中的Consumer下次会从offset = 9 的message 读取, Consumer Group B 中的Consumer下次会从offset = 11 的message 读取。

这里并没有说是Consumer A 下次会从offset = 9 的message读取,原因是Consumer A可能会退出Group ,然后Group A 进行rebalance,即重新分配分区。

Kafka的特点

1.Kafka作为消息系统

如何将Kafka的流的概念和传统的企业信息系统作比较?

消息处理模型历来有两种:队列和发布-订阅。在队列模型中,一组消费者可以从服务器读取记录,每个记录都会被其中一个消费者处理; 在发布-订阅模式里,记录被广播到所有的消费者。这两种模式都具有一定的优点和弱点。队列的优点是它可以让你把数据分配到多个消费者去处理,它可以让您扩展你的处理能力。不幸的是,队列不支持多个订阅者,一旦一个进程读取了数据,这个数据就会消失。发布-订阅模式可以让你广播数据到多个进程,但是因为每一个消息发送到每个订阅者,没办法对订阅者处理能力进行扩展。

Kafka的消费群的推广了这两个概念。消费群可以像队列一样让消息被一组进程处理(消费群的成员),与发布 – 订阅模式一样,Kafka可以让你发送广播消息到多个消费群。

Kafka的模型的优点是,每个主题都具有这两个属性,它可以扩展处理能力,也可以实现多个订阅者,没有必要二选一。

Kafka比传统的消息系统具有更强的消息顺序保证的能力。

传统的消息队列的消息在队列中是有序的,多个消费者从队列中消费消息,服务器按照存储的顺序派发消息。然而,尽管服务器是按照顺序派发消息,但是这些消息记录被异步传递给消费者,消费者接收到的消息也许已经是乱序的了。这实际上意味着消息的排序在并行消费中都将丢失。消息系统通常靠 “排他性消费”( exclusive consumer)来解决这个问题,只允许一个进程从队列中消费,当然,这意味着没有并行处理的能力。

Kafka做的更好。通过一个概念:并行性-分区-主题实现主题内的并行处理,Kafka是能够通过一组消费者的进程同时提供排序保证和负载均衡。每个主题的分区指定给每个消费群中的一个消费者,使每个分区只由该组中的一个消费者所消费。通过这样做,我们确保消费者是一个分区唯一的读者,从而顺序的消费数据。因为有许多的分区,所以负载还能够均衡的分配到很多的消费者实例上去。但是请注意,一个消费群的消费者实例不能比分区数量多。

2.Kafka作为存储系统

任何消息队列都能够解耦消息的生产和消费,还能够有效地存储正在传送的消息。Kafka与众不同的是,它是一个非常好的存储系统。

Kafka把消息数据写到磁盘和备份分区。Kafka允许生产者等待返回确认,直到副本复制和持久化全部完成才认为成功,否则则认为写入服务器失败。

Kafka使用的磁盘结构很好扩展,Kafka将执行相同的策略不管你是有50 KB或50TB的持久化数据。

由于存储的重要性,并允许客户控制自己的读取位置,你可以把Kafka认为是一种特殊用途的分布式文件系统,致力于高性能,低延迟的有保障的日志存储,能够备份和自我复制。

阅读 1.3k

推荐阅读
技术文档整理
用户专栏

后端开发

21 人关注
31 篇文章
专栏主页