【年后跳槽必看篇】Kafka 核心知识点 技术探秘第一章
基本概念
Kafka 是一个成熟的消息队列,是一个天然分布式、支持分区(partition)、多副本(replica)。是基于 Zookeeper 协调的分布式消息系统。它最大特性就是可以实时的处理大量数据以满足各种需求场景:比如:基于 Hadoop 的批处理系统,低延迟的实时系统、storm/Spark 流式处理引擎,web/nginx 日志、访问日志,消息服务等等。Kafka 使用 Scala 语言编写的。
Zookeeper 用于维护 Kafka 集群的状态和元数据信息,例如主题和分区的分配信息、消费者组和消费者偏移量等。
关于 Zookeeper 可以参考我之前的文章了解:
Kafka 的特性
高吞吐量、低延迟:Kafka 每秒可以处理几十万条消息,它的延迟最低只有几毫秒
可扩展性:Kafka 集群支持热扩展
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
容错性:允许集群中节点失败(若副本数量为 n,则允许 n-1 个节点失败)
高并发:支持数千个客户端同时读写
Kafka 的场景应用
日志收集:一个公司可以使用 Kafka 收集各种服务的 log,通过 Kafka 以通义接口服务的方式开放给各种 consumer,例如 Hadoop、Hbase、Solr 等。
消息系统:解耦、异步、削峰、分布式一致性等。关于为什么使用 MQ(为什么使用消息队列)可参考文章:
流式处理:比如:storm/Spark 流式处理引擎
Kafka 的架构是怎么样的
Kafka 的架构是整体设计比较简单,是显示的分布式架构,主要由 Producer(生产者)、broker(Kafka 集群)、和 consumer(消费者)组成。
如图所示:
Producer(生产者):生产者负责将消息发布到 Kafka 集群中的一个或多个 Topic(主题)中,每个 Topic 包含一个或多个 Partition(分区)
Topic:主题,是承载消息的逻辑容器,在实际使用中多用来区分具体的业务
Partition:分区,一个有序不变的消息序列。每个主题可以有多个分区
Consumer(消费者):消费者负责从 Kafka 集群中的一个或多个主题消费消息,并将消息的 offset(偏移量)提交回 Kafka 以保证消息的顺序性和一致性。
偏移量:offset,表示分区中每条消息的位置信息,是一个单调递增且不变的值
Kafka 集群:Kafka 集群是由多个 Kafka 节点(Broker)组成的分布式系统。每个节点都可以存储一个或多个 Topic(主题)的 Partiton(分区副本),以提高可用性和容错能力
Leader Broker:Leader Broker 是分区的副本,它是负责处理消息读写请求的节点。生产者将消息发送到 LeaderBroker,消费者从 Leader Broker 中拉取消息
Follower Broker:Follower Broker 是 Leader Broker 的备份节点,它负责与 LeaderBroker 进行数据同步,以保持自己的数据与 Leader Broker 一致
在集群中,每个分区都有一个 Leader Broker 和多个 Follower Broker,只有 Leader Broker 才能处理生产者和消费者的请求,而 Follower Broker 只是 Leader Broker 的备份,用于提供数据的冗余备份和容错能力。如果 Leader Broker 发生故障,Kafka 集群将会自动将 Follower Broker 提升为新的 Leader Broker,从而实现高可用性和容错能力
架构图:
Kafka 中比较重要的思想
ConsumerGroup(消费者群组):各个消费者 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 消息。两者对消息的生产和消费是异步的
Kafka 集群中 broker 之间的关系:不是主从关系,各个 broker 在集群中的地位是一样的,我们可以随意的增加或删除任何一个 broker 节点。
负载均衡方面:Kafka 提供了一个 metadata API 来管理 broker 之间的负载(仅对 Kafka 0.8.x 而言,对于 0.7.x 主要是靠 zookeeper 来实现负载均衡)
同步异步:producer 采用异步 push 方式,极大提高了 Kafka 系统的吞吐量(并且可以通过参数控制是采用同步还是异步方式)
分区机制 partition:Kafka 的 broker 端支持消息分区,producer 可以决定把消息发送到哪个分区,在一个分区中消息的顺序就是 producer 发送消息的顺序,一个主题中可以有多个分区(partition),具体分区数量也是可配置的。分区的意义很大。
离线数据装载:Kafka 由于对可扩展的数据持久化的支持,它也非常适合向 Hadoop 或者数据仓库中进行数据装载
插件支持:现在已经有不少活跃社区开发出很多插件来支持扩展 Kafka 的功能,如用来配合 Storm、Hadoop、Flume 等相关的插件。
当我们需要自己设计一个 MQ 的时候也可以从上述比较好的思想中提炼出我们所需要的:
关于如何写一个消息队列,该如何进行架构设计,可参考文章:场景题-如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。
Kafka 为什么这么快
消息发送方面:
批量发送:Kafka 通过将多个消息大巴拼成一个批次,减少了网络传输和磁盘写入的次数,从而提高了消息的吞吐量和传输效率
异步发送:生产者可以异步发送消息,不必等待每个消息的确认,这大大提高了消息发送的效率
消息压缩:支持对消息进行压缩,减少网络传输的数据量
并行发送:通过将数据分别不在不同的分区(Partitions),生产者可以并行发送这些消息,从而提高了吞吐量
消息存储方面:
零拷贝技术:Kafka 使用零拷贝技术来避免了数据的拷贝操作性能问题,降低了内存和 CPU 的使用率,提高了系统的性能
磁盘顺序写入:Kafka 把消息存储在磁盘上,且以顺序的方式写入数据。顺序写入比随机写入速度快很多,因为它减少了磁头寻道时间。避免了随机读写带来的性能损耗,提高了磁盘的使用效率
页缓存:Kafka 将其数据存储在磁盘中,但在访问数据时,它会先将数据加载到操作系统中的页缓存中,并在页缓存中保留一份副本,从而实现快速的数据访问。
稀疏索引:Kafka 存储消息是通过分段的日志文件,每个分段都有自己的索引。这些索引文件中的条目不是对分段中的每条消息都建立索引,而是每隔一定数量的消息建立一个索引点,这就构成了稀疏索引。稀疏索引减少了索引大小,使得加载内存中的索引更小,提高了查找特定消息的效率
分区和副本:Kafka 采用分区和副本的机制,可以将数据分散到多个节点上进行处理,从而实现了分布式的高可用性和负载均衡
消息消费方面:
消费者群组:通过消费者群组可以实现消息的负载均衡和容错处理
并行消费:不同的消费者可以独立地消费不同的分区,实现消费的并行处理
批量拉取:Kafka 支持批量拉取消息,可以一次性拉取多个消息进行消费。减少网络消耗,从而提升性能
Kafka 如何保证消息不丢失
正常情况下,消息丢失大概分为三种情况:
生产者消息丢失(Producer 端发送消息到 Kafka Broker 时丢失)
Kafka(MQ)本身将消息弄丢了(Kafka 处理消息进行同步持久化时失败)
消费者消费的时候消息丢失(Consumer 从 Kafka Broker 端拉取数据进行消费出现异常)
注意:**Kafka 只对已提交的消息做最大限度地持久化保证不丢失,但是办法保证 100%。**后面会讲
Producer(生产者)角度
消息的生产者,消息发送给 Kafka 集群的过程中有可能会出现异常失败。所以需要有机制来确保消息能够成功发送。(但是还是存在网络波动的问题无法保证一次消息一定能发送成功)。如果没有从成功需要重新发送知道成功。
我们在使用 Kafka 发送消息的时候,通常使用的时producer.send(msg)
来发送消息,这是一种异步发送
,发送消息的时候方法会立即返回,但不一定代表消息发送成功了。当时方法prodcuer.send(msg).get()
是同步等待返回的。
所以我们通常为了保证消息在发送不丢失,会建议使用producer.send(msg, callback)
方法,这个方法支持传入一个 callback,我们可以在消息发送的时候进行重试。同时 Producer 还提供了一些配置参数来提升发送成功率:
acks = 0:表示 Producer 请求立即返回,不需要等待 Leader 的任何确认。这种方案有最高的吞吐率,但是不保证消息是否真的发送成功
acks = -1:等价于(acks = all),表示分区 Leader 必须要等待消息被成功写入到所有的 ISR 副本(同步副本)中才认为 Producer 请求成功。这种方案提供最高的消息持久性保证,但是理论上吞吐率也是最差的。
acks = 1:表示 Leader 副本必须应答此 Producer 请求并写入消息到本地日志,之后 Producer 请求被认为成功。如果此时 Leader 副本应该请求之后挂掉了,消息会丢失。这个方案,提供了不错的持久性保证和吞吐
Kafka 本身丢失消息(broker 集群)
在实际业务场景会存在 Kafka 的 Leader 接收到了消息,但是还没有来得及同步给 Follower 就挂掉了,此时 Follower 变成了 Leader,导致数据丢失。
在 Kafka 集群中有一些机制来保证消息的不丢失,比如:复制机制、持久化存储机制以及 ISR 机制。
持久化存储:Kafka 使用持久化存储来存储消息。这意味着消息在写入 Kafka 时将被写入磁盘,这种方式可以防止消息因为节点宕机而丢失。
ISR 复制机制:Kafka 使用 ISR 机制来确保消息不会丢失,Kafka 使用复制机制来保证数据的可靠性。每个分区都有多个副本,副本可以分布在不同的节点上。当一个节点宕机时,其它节点上的副本仍然可以提供服务,保证消息不丢失。
当然在 Kafka 中还提供了一些配置参数来避免消息丢失的问题:
消费者角度消息丢失(Consumer)
消费者消费消息的时候,消息还没有处理完成,便自动提交了 offset。导致消息没有消费丢失掉。
所以就需要保证不要乱提交 offset 就行了。在这方面 Kafka 消费者会跟踪每个分区的 offset(偏移量),消费者每次消费消息时,都会将 offset 向后移动。当消费者宕机或者不可用时,Kafka 会将该消费者所消费的分区的 offset 保存下来,下次该消费者重新启动时,可以从上一次 offset 重新开始消费
另外,Kafka 消费者还可以组成消费者组,每个消费者组可以同时消费多个分区。当一个消费者组中的消费者宕机或者不可用时,其他消费者仍然可以消费该组的分区,保证消息不丢失。
**同时也可以关闭自动提交 offset,去手动提交 offset,**避免拉取了消息以后,业务逻辑没处理完,提交偏移量后但是消费者挂了的问题:
如有问题,欢迎加微信交流:w714771310,备注- 技术交流 。或关注微信公众号【码上遇见你】。
好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。
版权声明: 本文为 InfoQ 作者【派大星】的原创文章。
原文链接:【http://xie.infoq.cn/article/90b4f28b07d004672b6a87c40】。
本文遵守【CC BY-NC-ND】协议,转载请保留原文出处及本版权声明。
评论