文章目录
- 01. 什么是 Kafka?
- 02. 为什么要用kafka?
- 03. Kafka 消息引擎模型
- 04. kafka 消费方式?
- 05. Kafka 传输消息的编码格式?
- 06. kafka 体系架构?
- 07. kafka 消息和批次?
- 08. kafka 主题和分区?
- 09. kafka 分区和副本?
- 10. kafka 生产者?
- 11. kafka 消费者?
- 12. kafka 消费者组?
- 13. kafka broker和集群?
- 14. kafka 相关术语总结
01. 什么是 Kafka?
Kafka是一个分布式流处理平台,最初由LinkedIn开发。它是一个高吞吐量、低延迟的消息队列系统,可以处理大量的实时数据流。Kafka的设计目标是为了处理实时数据流,包括日志、指标、事件和其他实时数据。它的架构基于发布/订阅模式,其中生产者将消息发布到主题(topic)中,而消费者则从主题中订阅消息。Kafka的主要特点包括高吞吐量、可扩展性、持久性、容错性和可靠性。
02. 为什么要用kafka?
其实主要就是两个方面:削峰填谷和异步通讯,当然什么解耦呀,减少冗余啊,业务代码的健壮性啊也是有的,可是生产环境中,主要看的还是削峰和异步通讯这两个方面的功能。
① 削峰填谷:
所谓的“削峰填谷”就是指缓冲上下游瞬时突发流量,使其更平滑。特别是对于那种发送能力很强的上游系统,如果没有消息引擎的保护,“脆弱”的下游系统可能会直接被压垮导致全链路服务“雪崩”。但是,一旦有了消息引擎,它能够有效地对抗上游的流量冲击,真正做到将上游的“峰”填满到“谷”中,避免了流量的震荡。
应用场景主要是上游数据某些时候有突发大流量数据,下游可能扛不住,或者下游没有足够多的机器来保证冗余,这个时候,kafka就可以在中间起到一个缓冲的作用,它可以把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。
② 异步通信:
消息引擎系统的另一大好处在于发送方和接收方的松耦合,这也在一定程度上简化了应用的开发,减少了系统间不必要的交互。
比如,极客付费课程都有一个专门的订阅按钮,点击之后进入到付费页面。这个简单的流程中就可能包含多个子服务,比如点击订阅按钮会调用订单系统生成对应的订单,而处理该订单会依次调用下游的多个子系统服务 ,比如调用支付宝和微信支付的接口、查询你的登录信息、验证课程信息等。显然上游的订单操作比较简单,它的 TPS 要远高于处理订单的下游服务,因此如果上下游系统直接对接,势必会出现下游服务无法及时处理上游订单从而造成订单堆积的情形。特别是当出现类似于秒杀这样的业务时,上游订单流量会瞬时增加,可能出现的结果就是直接压跨下游子系统服务。
应用场景主要是很多时候用户不需要立刻处理消息,消息队列就提供了异步处理机制,允许用户把一个消息放入队列,但是并不立即处理,等啥时候需要了再去处理这些消息,这样可以极大程度的缓解性能问题。
③ 解决方法:
解决此问题的一个常见做法是我们对上游系统进行限速,但这种做法对上游系统而言显然是不合理的,毕竟问题并不出现在它那里。所以更常见的办法是引入像 Kafka 这样的消息引擎系统来对抗这种上下游系统 TPS 的错配以及瞬时峰值流量。
当引入了 Kafka 之后。上游订单服务不再直接与下游子服务进行交互。当新订单生成后它仅仅是向 Kafka Broker 发送一条订单消息即可。类似地,下游的各个子服务订阅 Kafka 中的对应主题,并实时从该主题的各自分区(Partition)中获取到订单消息进行处理,从而实现了上游订单服务与下游订单处理服务的解耦。这样当出现秒杀业务时,Kafka 能够将瞬时增加的订单流量全部以消息形式保存在对应的主题中,既不影响上游服务的 TPS,同时也给下游子服务留出了充足的时间去消费它们。这就是 Kafka 这类消息引擎系统的最大意义所在。
④ 项目场景:
对于安全告警和安全事件的操作都会有对应的处置记录,比如在列表页面选择安全告警发起工单,标为处置中,标为误报,标为忽略等操作,soar剧本动作节点中调整安全告警危害等级,执行soar,调整安全告警确定性等级,调整安全告警攻击结果,调整安全告警处置状态,调整资产的基本信息,调整资产责任人等动作都会执行写处置记录操作。由于处置记录操作在多处调用,我们并不希望在各处调用写处置记录API,且对于安全告警的这些操作动作而言,写处置记录并不需要立刻去处理,因此可以引入kafka消息队列,当动作执行完成后就封装一条对应的处置记录信息发送到Kafka Broker,kafka消费者订阅对应主题,并实时从主题的分区中获取到处置记录消息进行处理,从而实现了上游安全告警业务服务与下游处置记录处理服务的解耦,减少了系统间不必要的交互。
03. Kafka 消息引擎模型
消息引擎模型,即我用什么方法把消息传输出去,常见的有两种方法:
① 点对点模型:生产者发送一条消息到 Queue,只有一个消费者能收到,Queue 实现了负载均衡。
- 生产者生产消息发送到 Queue 中,消费者从 Queue 中取出并消费消息。
- 消息被消费以后,Queue 中不再存储,所以消费者不可能消费到已经被消费的消息。
- Queue 支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。 比如:生产者发送 100 条消息的话,两个消费者来消费一般情况下两个消费者会按照消息发送的顺序各自消费一半。
② 发布订阅模型:发布者发送到 topic 的消息,只有订阅了 topic 的订阅者才会收到消息。
- 消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。
- 和点对点模型不同的是,可以存在多个发布者向相同的主题发送消息,而订阅者也可能存在多个,它们都能接收到相同主题的消息。
③ kafka的消息引擎模型:Kafka 同时支持以上两种消息引擎模型。
Kafka中存在消费者组的概念,主题的单个分区只能分配给消费者组内的某个Consumer实例消费,当然这个分区也可以被其他的 Group 消费。
针对点对点模型和发布 / 订阅模型,前者也称为消费队列。传统的消息队列模型的缺陷在于消息一旦被消费,就会从队列中被删除,而且只能被下游的一个 Consumer 消费。严格来说,这一点不算是缺陷,只能算是它的一个特性。但很显然,这种模型的伸缩性很差,因为下游的多个 Consumer 都要“抢”这个共享消息队列的消息。发布 / 订阅模型倒是允许消息被多个 Consumer 消费,但它的问题也是伸缩性不高,因为每个订阅者都必须要订阅主题的所有分区。这种全量订阅的方式既不灵活,也会影响消息的真实投递效果。
如果有这么一种机制,既可以避开这两种模型的缺陷,又兼具它们的优点,那就太好了。幸运的是,Kafka 的 Consumer Group 就是这样的机制。当 Consumer Group 订阅了多个主题后,组内的每个实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息。
Consumer Group 之间彼此独立,互不影响,它们能够订阅相同的一组主题而互不干涉。再加上 Broker 端的消息留存机制,Kafka 的 Consumer Group 完美地规避了上面提到的伸缩性差的问题。可以这么说,Kafka 仅仅使用 Consumer Group 这一种机制,却同时实现了传统消息引擎系统的两大模型:如果所有实例都属于同一个 Group,那么它实现的就是消息队列模型;如果所有实例分别属于不同的 Group,那么它实现的就是发布 / 订阅模型。
04. kafka 消费方式?
Kafka消息的消费方式有两种:拉取(pull)和推送(push)。
拉取方式:消费者通过轮询(poll)的方式从Kafka集群中拉取消息。消费者可以控制拉取的频率和数量,可以自由地控制消息的消费速度。这种方式需要消费者主动去拉取消息,因此消费者需要不断地轮询Kafka集群,这会增加一定的网络开销和CPU负载。如果 kafka 没有数据,消费者可能会陷入循环中, 一直返回空数据。 针对这一点, Kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有数据可供消费, consumer 会等待一段时间之后再返回,这段时长即为 timeout。
推送方式:消费者通过订阅(subscribe)的方式注册到Kafka集群中,当有新的消息到达时,Kafka会主动推送消息给消费者。这种方式不需要消费者主动去拉取消息,因此可以减少网络开销和CPU负载。但是,由于消费者无法控制消息的推送速度,可能会导致消息积压或者消息丢失的问题。
在实际应用中,一般会根据具体的业务需求和系统架构选择合适的消费方式。
05. Kafka 传输消息的编码格式?
Kafka传输消息的编码格式是二进制格式。具体来说,消息被序列化为字节数组,然后通过网络传输。Kafka支持多种序列化器,包括JSON、Avro、Protobuf等,但它们最终都会被序列化为字节数组。在消费者端,消息会被反序列化为原始的数据格式。Kafka的二进制格式具有高效、可扩展、灵活等特点,使得它能够处理大量的消息,并支持多种数据类型和编码方式。
06. kafka 体系架构?
Kafka 是一个分布式的流处理平台,它的体系架构主要包括以下几个组件:
① Broker:Kafka 集群中的每个节点都是一个 Broker,它们负责存储和处理消息。每个 Broker 可以处理多个 Topic 的消息。
② Topic:消息的逻辑分类,每个 Topic 可以有多个 Partition,每个 Partition 可以在不同的 Broker 上。
③ Partition:每个 Topic 可以被分成多个 Partition,每个 Partition 是一个有序的消息队列,它们可以在不同的 Broker 上,每个 Partition 只能被一个 Consumer Group 中的一个 Consumer 消费。
④ Producer:生产者,负责向 Kafka 集群中的 Topic 发送消息。
⑤ Consumer:消费者,负责从 Kafka 集群中的 Topic 消费消息。
⑥ Consumer Group:消费者组,由多个 Consumer 组成,它们共同消费一个或多个 Topic 的消息,每个 Consumer Group 中的 Consumer 只能消费一个 Partition 中的消息。
Kafka 的体系架构是高度可扩展的,可以通过增加 Broker 和 Partition 来扩展集群的容量和吞吐量。同时,Kafka 还提供了多种可靠性保障机制,如数据备份、数据复制和数据恢复等,以确保数据的可靠性和高可用性。
07. kafka 消息和批次?
Kafka 是一个分布式的消息系统,它的消息是以批次(batch)的形式进行传输的。批次是指一组消息,这些消息可以一起发送到 Kafka 集群中的一个分区(partition)中。批次的大小可以通过配置参数进行设置,通常情况下,批次的大小是几千到几万条消息。
批次的使用可以提高 Kafka 的吞吐量和性能,因为批次中的消息可以一起进行压缩和传输,从而减少网络传输的开销。此外,批次还可以提高消息的可靠性,因为一组消息要么全部发送成功,要么全部发送失败,这样可以避免消息的丢失或重复发送。
在 Kafka 中,消息是以键值对的形式进行存储和传输的。每个消息都有一个键和一个值,键和值都是字节数组。在发送消息时,可以指定消息的键和值,这样可以方便地对消息进行分类和过滤。在接收消息时,可以根据键来选择需要处理的消息。
总之,Kafka 的消息和批次机制是其高性能和高可靠性的关键所在,它可以满足大规模数据处理和实时数据流处理的需求。
08. kafka 主题和分区?
Kafka是一个分布式流处理平台,它通过将数据分成多个主题(Topic)和分区(Partition)来实现高效的数据处理和传输。分区的作用是将主题的消息分散到多个节点上进行存储和处理,从而提高系统的可伸缩性和容错性。
Kafka中的消息以主题为单位进行归类,生产者可以将数据发送到指定的主题和分区,消费者可以订阅指定的主题和分区来获取数据。主题是Kafka中数据的逻辑分类,类似于数据库中的表。每个主题可以被分成多个分区,分区在存储层面可以看作一个可追加的日志文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)。offset是消息在分区中的唯一标识,Kafka通过它来保证消息在分区内的顺序性,不过offset并不跨越分区,由于一个主题一般包含几个分区,因此无法在整个主题范围内保证消息的顺序,但可以保证消息在单个分区内是有序的。
如图,主题中有 4 个分区,消息被顺序追加到每个分区日志文件的尾部。Kafka中的分区可以分布在不同的服务器(broker)上,也就是说,一个主题可以横跨多个broker,以此来提供比单个broker更强大的性能。每一条消息被发送到broker之前,会根据分区规则选择存储到哪个具体的分区。如果分区规则设定得合理,所有的消息都可以均匀地分配到不同的分区中。如果一个主题只对应一个文件,那么这个文件所在的机器 I/O 将会成为这个主题的性能瓶颈,而分区解决了这个问题。在创建主题的时候可以通过指定的参数来设置分区的个数,当然也可以在主题创建完成之后去修改分区的数量,通过增加分区的数量可以实现水平扩展。
09. kafka 分区和副本?
Kafka 为分区引入了多副本(Replica)机制,通过增加副本数量可以提升容灾能力。同一分区的不同副本中保存的是相同的消息,每个分区都有一个 leader 副本和多个 follower 副本,其中leader副本负责处理读写请求,follower副本只负责与leader副本的消息同步。副本处于不同的broker中,当leader副本出现故障时,Kafka 会自动从 follower 副本中选举一个新的 leader 副本对外提供服务。这个过程称为副本重分配。在副本重分配期间,Kafka 仍然可以正常工作,但可能会出现一些延迟。Kafka通过多副本机制实现了故障的自动转移,当Kafka集群中某个broker失效时仍然能保证服务可用。
如图,Kafka集群中有4个broker,某个主题中有3个分区,且副本因子(即副本个数)也为3,如此每个分区便有1个leader副本和2个follower副本。生产者和消费者只与leader副本进行交互,而follower副本只负责消息的同步,很多时候follower副本中的消息相对leader副本而言会有一定的滞后。
Kafka 的副本机制可以提供以下好处:
① 数据可靠性:即使某个副本失效,数据仍然可以从其他副本中恢复。
② 高可用性:即使某个副本失效,Kafka 仍然可以正常工作。
③ 提高读取性能:由于可以从任意一个副本中读取消息,因此可以提高读取性能。
Kafka 消费端也具备一定的容灾能力。Consumer 使用拉(Pull)模式从服务端拉取消息,并且保存消费的具体位置,当消费者宕机后恢复上线时可以根据之前保存的消费位置重新拉取需要的消息进行消费,这样就不会造成消息丢失。
10. kafka 生产者?
生产者创建消息,一条消息会被发布到一个特定的主题上。在默认情况下,生产者会把消息均衡地分布到主题的所有分区中。不过,在某些情况下,生产者会把消息直接写入指定的分区,这通常是通过消息键和分区器来实现的。分区器会为键生成一个哈希值,并将其映射到指定的分区,这样可以保证包含同一个键的消息被写入同一个分区。生产者也可以使用自定义的分区器,根据不同的业务规则将消息映射到不同的分区。
11. kafka 消费者?
消费者读取消息,消费者会订阅一个或多个主题,并按照消息写入分区的顺序读取它们。消费者通过检查消息的偏移量来区分已经读取过的消息。偏移量(不断递增的整数值)是另一种元数据,在创建消息时,Kafka会把它添加到消息里。在给定的分区中,每一条消息的偏移量都是唯一的,越往后消息的偏移量越大(但不一定是严格单调递增)。消费者会把每一个分区可能的下一个偏移量保存起来(通常保存在Kafka中),如果消费者关闭或重启,则其读取状态不会丢失。
12. kafka 消费者组?
消费者可以是消费者群组的一部分,属于同一群组的一个或多个消费者共同读取一个主题。群组可以保证每个分区只被这个群组里的一个消费者读取。如图,有3个消费者同时读取一个主题,其中的两个消费者各自读取3个分区中的1个分区,另外一个消费者读取其他2个分区。
通过这种方式,消费者可以读取包含大量消息的主题。而且,如果一个消费者失效,那么群组里的其他消费者可以接管失效消费者的工作。
13. kafka broker和集群?
Kafka broker是Kafka集群中的一个节点,它负责处理客户端的请求,包括读取和写入数据。每个broker都有一个唯一的标识符,称为broker ID,它在整个集群中必须是唯一的。Kafka broker之间通过Zookeeper进行协调和通信,以确保集群的高可用性和可靠性。
14. kafka 相关术语总结
Kafka 属于分布式的消息引擎系统,它的主要功能是提供一套完备的消息发布与订阅解决方案。在 Kafka 中,发布订阅的对象是主题(Topic),你可以为每个业务、每个应用甚至是每类数据都创建专属的主题。
向主题发布消息的客户端应用程序称为生产者(Producer),生产者程序通常持续不断地向一个或多个主题发送消息,而订阅这些主题消息的客户端应用程序就被称为消费者(Consumer)。和生产者类似,消费者也能够同时订阅多个主题的消息。我们把生产者和消费者统称为客户端(Clients)。你可以同时运行多个生产者和消费者实例,这些实例会不断地向 Kafka 集群中的多个主题生产和消费消息。
有客户端自然也就有服务器端。Kafka 的服务器端由被称为 Broker 的服务进程构成,即一个 Kafka 集群由多个 Broker 组成,Broker 负责接收和处理客户端发送过来的请求,以及对消息进行持久化。虽然多个 Broker 进程能够运行在同一台机器上,但更常见的做法是将不同的 Broker 分散运行在不同的机器上,这样如果集群中某一台机器宕机,即使在它上面运行的所有 Broker 进程都挂掉了,其他机器上的 Broker 也依然能够对外提供服务。这其
实就是 Kafka 提供高可用的手段之一。
实现高可用的另一个手段就是备份机制(Replication)。备份的思想很简单,就是把相同的数据拷贝到多台机器上,而这些相同的数据拷贝在 Kafka 中被称为副本(Replica)。好吧,其实在整个分布式系统里好像都叫这个名字。副本的数量是可以配置的,这些副本保存着相同的数据,但却有不同的角色和作用。Kafka 定义了两类副本: 领导者副本(LeaderReplica)和追随者副本(Follower Replica)。前者对外提供服务,这里的对外指的是与客户端程序进行交互;而后者只是被动地追随领导者副本而已,不能与外界进行交互。当然了,你可能知道在很多其他系统中追随者副本是可以对外提供服务的,比如 MySQL 的从库是可以处理读操作的,但是在 Kafka 中追随者副本不会对外提供服务。
副本的工作机制也很简单:生产者总是向领导者副本写消息;而消费者总是从领导者副本读消息。至于追随者副本,它只做一件事:向领导者副本发送请求,请求领导者把最新生产的消息发给它,这样它能保持与领导者的同步。
虽然有了副本机制可以保证数据的持久化或消息不丢失,但没有解决伸缩性的问题。什么是伸缩性呢?我们拿副本来说,虽然现在有了领导者副本和追随者副本,但倘若领导者副本积累了太多的数据以至于单台 Broker 机器都无法容纳了,此时应该怎么办呢?一个很自然的想法就是,能否把数据分割成多份保存在不同的 Broker 上?如果你就是这么想的,那么恭喜你,Kafka 就是这么设计的。
这种机制就是所谓的分区(Partitioning)。如果你了解其他分布式系统,你可能听说过分片、分区域等提法,比如 MongoDB 和 Elasticsearch 中的 Sharding、HBase 中的Region,其实它们都是相同的原理,只是Partitioning 是最标准的名称。
Kafka 中的分区机制指的是将每个主题划分成多个分区(Partition),每个分区是一组有序的消息日志。生产者生产的每条消息只会被发送到一个分区中,也就是说如果向一个双分区的主题发送一条消息,这条消息要么在分区 0 中,要么在分区 1 中。如你所见,Kafka的分区编号是从 0 开始的,如果 Topic 有 100 个分区,那么它们的分区号就是从 0 到99。
副本如何与这里的分区联系在一起呢? 实际上,副本是在分区这个层级定义的。每个分区下可以配置若干个副本,其中只能有 1 个领导者副本和 N-1 个追随者副本。生产者向分区写入消息,每条消息在分区中的位置信息由一个叫位移(Offset)的数据来表征。分区位移总是从 0 开始,假设一个生产者向一个空分区写入了 10 条消息,那么这 10 条消息的位移依次是 0、1、2、…、9。
至此我们能够完整地串联起 Kafka 的三层消息架构:
① 第一层是主题层,每个主题可以配置 M 个分区,而每个分区又可以配置 N 个副本。
② 第二层是分区层,每个分区的 N 个副本中只能有一个充当领导者角色,对外提供服务;其他 N-1 个副本是追随者副本,只是提供数据冗余之用。
③ 第三层是消息层,分区中包含若干条消息,每条消息的位移从 0 开始,依次递增。
④ 最后,客户端程序只能与分区的领导者副本进行交互。
我们来说说 Kafka Broker 是如何持久化数据的。总的来说,Kafka 使用消息日志(Log)来保存数据,一个日志就是磁盘上一个只能追加写(Append-only)消息的物理文件。因为只能追加写入,故避免了缓慢的随机 I/O 操作,改为性能较好的顺序I/O 写操作,这也是实现 Kafka 高吞吐量特性的一个重要手段。不过如果你不停地向一个
日志写入消息,最终也会耗尽所有的磁盘空间,因此 Kafka 必然要定期地删除消息以回收磁盘。怎么删除呢?简单来说就是通过日志段(Log Segment)机制。在 Kafka 底层,一个日志又近一步细分成多个日志段,消息被追加写到当前最新的日志段中,当写满了一个日志段后,Kafka 会自动切分出一个新的日志段,并将老的日志段封存起来。Kafka 在后台还有定时任务会定期地检查老的日志段是否能够被删除,从而实现回收磁盘空间的目的。
在 Kafka 中实现 P2P 模型的方法是引入了消费者组(Consumer Group)。所谓的消费者组,指的是多个消费者实例共同组成一个组来消费一组主题。这组主题中的每个分区都只会被组内的一个消费者实例消费,其他消费者实例不能消费它。为什么要引入消费者组呢?主要是为了提升消费者端的吞吐量。多个消费者实例同时消费,加速整个消费端的吞吐量(TPS)。
消费者组里面的所有消费者实例不仅“瓜分”订阅主题的数据,而且更酷的是它们还能彼此协助。假设组内某个实例挂掉了,Kafka 能够自动检测到,然后把这个 Failed 实例之前负责的分区转移给其他活着的消费者。这个过程就是 Kafka 中大名鼎鼎的“重平衡”(Rebalance)。
每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上,这个字段就是消费者位移(Consumer Offset)。注意,这和上面所说的位移完全不是一个概念。上面的“位移”表征的是分区内的消息位置,它是不变的,即一旦消息被成功写入到一个分区上,它的位移值就是固定的了。而消费者位移则不同,它可能是随时变化的,毕竟它是消费者消费进度的指示器嘛。另外每个消费者有着自己的消费者位移,因此一定要区分这两类位移的区别。我个人把消息在分区中的位移称为分区位移,而把消费者端的位移称为消费者位移。