原文链接:https://sookocheff.com/post/kafka/kafka-in-a-nutshell/
kakfa是一个消息系统。就这样。那为啥这么大肆宣传呢?事实上,在系统间传输数据时,消息是一个很重要的基础结构。想知道为什么,让我们看看没有消息系统的数据管道。
这个系统起源于使用Hadoop进行存储和数据处理。Hadoop在没有数据时不是很有用,所以第一步是使用Hadoop获取数据。
至此,还没啥。不幸的是,现实世界中的数据并行存在多个系统中,而且需要进行系统间或与Hadoop间的交互。情况马上就变复杂了,多个数据系统间要进行沟通需要通过很多的信道。这些信道要求它们自定义协议和沟通方式,使得这些系统间的数据传输可能会占用一组开发者的全部时间。
再看看这张图,使用Kafka作为中心信息总线。所有输入的数据都先放入Kafka,所有输出的数据都从Kafka读出。Kafka成为了数据生产者和数据消费者交互的中心。
什么是Kafka?
Kafka是一个通过发布-订阅模型提供快速、高扩展和高吞吐量的分布式消息传输系统。Kafka的分布式设计提供了多种优势。第一,Kafka接受大量的永久或特定的消费者;第二,Kafka具有高可用性,能接受节点失败且支持自动恢复。在实际的数据系统中,这些特性使得Kafka完美符合大型数据系统组件之间通信和集成的要求。
Kafka 术语
Kafka的基础架构由这么几个部件组成:主题(topics)
、生产者(producers)
、消费者(consumers)和brokers
。
所有的Kafka消息都会被归类为topics
。如果你想发送消息,你可以将其发送到特定topic;如果想读取消息,则可以从特定topic读取。只有当producers
推送消息到Kafka的topic,consumer
才能从该Kafka topic中获取消息。最后,Kafka作为一个分布式系统,可以使用集群。集群中的每个节点称为broker
。
Kafka主题剖析
Kafka主题被分为了几个分区(partitions)
。分区允许通过多个brokers拆分特定主题的数据实现topic并发,每个分区都能放在一台独立的机器上,并允许多个消费者并行读取一个主题。消费者也可以并行化,所以,多个消费者可以读取一个主题的多个分区以实现消息处理的高吞吐量。
分区中的每个消息都有个标识称为偏移(offset)
。消息的偏移顺序是一个不可变的序列。Kafka为你维持了这个消息顺序。消费者可以从一个特定的偏移开始读取消息,Kafka允许他们从选择的任意偏移节点读取,也允许消费者在他们认为合适的时间点加入集群。考虑这些限制,Kafka集群中的每条特定消息都可以被这么组元组唯一标识:[topic, partition, offset]
查看分区的另一种方法是日志。一个数据源将消息写入日志,一个或多个消费者在他们选择的时间读取日志。下图中一个数据源正在向日志中写数据,消费者A和B正在不同的偏移中读取日志。
Kafka保留消息的时间是可以配置的,并且消费者可以据此自行调整读取行为。例如,如果Kafka配置成保留消息一天,而一个消费者超过一天后消费,该消费者将会丢失消息。如果消费者一个小时后消费,他能从上次知道的偏移处再次开始读取信息。从Kafka的角度看,它不保存消费者读取某个主题的状态。
Partitions and Brokers
每个broker可以维持多个分区,而且每个分区可以是一个主题的leader或replica。所有对主题的读写都通过leader,然后leader协调向replica更新新数据。如果leader发生了故障,会选择一个replica成为新leader。
生产者
生产者只写向一个leader,这提供了一种负载均衡的生产方式,即每个写操作能由不同的broker和机器负责。在第一张图中生产者写向主题的分区0,然后分区0的将备份到其它可用replicas。
在第二张图中生产者写向主题的分区1,然后分区1将其备份到其它可用replicas。
由于一台机器负责一次写操作,系统整体的吞吐量是提升的。
消费者和消费群组
消费者可以从任意一个分区读,其提升消息消费吞吐量的方式与生产消息类似。对某一主题而言,消费者也能够组成消费群组,组中的每一个消费者从单独的分区读,而消费组整体消费整个主题的消息。如果消费者的数量多于分区的数量,那么有些消费者会因为没有分区可读而空闲。如果分区数量多于消费者数量,那么消费者将接收多个分区的消息。如果消费者跟分区数量一样多,每个消费者按顺序地从一个分区中读取消息。
下面这张图来自Kafka文档,描述了一个主题多个分区的情况。其中,服务器1有分区0和3,服务器2有分区1和2。有两个消费者组A和B。A由两个消费者组成,B由4个消费者组成。A组两个消费对应4个分区——每个消费者从两个分区读。B组消费者与分区数目一致,每个消费者从一个分区读。
一致性和可用性
在讨论一致性和可用性之前,需要谨记下面的保证基于一个前提:只向一个分区写,只从一个分区读。如果有两个消费者从同一分区读或两个生产者向同一分区写,那么这些保证将失效。
Kafka对数据一致性和可用性做出以下保证:
- 消息写入一个主题分区将按照写入顺序依次追加到确认日志
- 单个消费者实例将按照日志中的顺序读到消息
- 当所有同步replica都将消息写入日志,该消息才算被“确认”
- 只要一个同步replica还存活着,确认的消息就不会被丢失
前两条保证确保了消息在每个分区中是有序的。提醒一下,这并不确保整个主题中的消息是有序的。后两条保证确保了确认的消息是可以查找到的。在Kafka中,被选为leader的分区负责将收到的消息同步给replica。当一个replica收到了消息,就认为该replica是同步的。为了更好地理解这个,我们进一步看看在写的过程中发生了什么。
处理写
当与Kafka集群通信时,所有的消息都发送给了leader分区。leader负责向同步replica写消息,一旦该消息被确认,再负责向不同broker上的其余replica传播。每个确认收到了该消息的replica,都可称为是同步的。
当集群中的所有broker都是可用的,消费者和生产者能轻松无误地从主题的leader分区中读写。不幸的是,leader和replica都是可能发生故障的,我们需要去处理这些状况。
处理故障
当一个replica故障时会发生什么?写操作送不到故障的replica,并且其不再能接受消息,会远远落后于leader。如下图,replica 3不再能从leader接收消息。
当第二个replica故障时又会发生什么?第二个replica同样不能接收消息,而且也会落后于leader。
此时,只有leader处于同步状态。在Kafka的概念中,我们还有一个同步的replica,虽然该replica是分区的leader。
leader挂了又会发生什么呢?我们只留下了3个挂了的replica。
replica 1实际上还是处于同步状态的,虽然它不能接收任何新的消息,但是它在能接收到消息的时候是同步的。replica 2会丢失一些数据,replica 3(最先挂的那位)会丢失更多的数据。在这种状况下,有两种解决方案。
- 第一种最简单的方案是在继续之前等待leader恢复。一旦leader恢复,它将再次开始读写消息,等replica恢复后,它们将再次与leader进行同步。
- 第二种是选择另一个broker恢复并选其为leader。这个broker是落后于现存的leader的,而且所有在该broker挂掉到它被选为leader这段时间写入的消息都会丢失。别的broker恢复的时候,会发现它们有些已确认的消息在新的leader中是没有的,它们会丢掉这些消息。虽然选择新的leader会丢失一些消息,但是我们可以尽可能快地选择任意机器成为leader,以此来降低损耗。
返回之前,我们来看看当leader挂了,但同步replica还正常的情况。
在这种情况下,Kafka控制器将检测到leader挂机了,会从同步replica中选出一位新的leader。这会消耗几秒钟,会导致客户端显示LeaderNotAvailable
错误。但是,只要生产者和消费者发现这种情况后正常地重试,就不会有数据损失。
Kafka客户端的一致性
Kafka客户端有两种:生产者和消费者。二者都有不同级别的一致性可以配置。
对于一条消息,生产者有三种选择:
- 等所有同步replica都获得该消息
- 只等leader获取该消息
- 不等
这几种方法都有自己的优缺点,使用者可以根据系统的一致性和吞吐量来决定合适的策略。
对于消费者来说,只能读到已确认的消息(例如:那些已经被写入所有同步replica的消息)。基于此情况,我们同样为消费者提供了3个级别的一致性选择:
- 每条消息至多接收一次
- 每条消息至少接收一次
- 每条消息只接受一次
需要自行确定使用哪种。
至多接受一次消息时,消费者从一个分区中读出数据,确认了已读的消息(offset),然后处理消息。如果消费者在确认消息和处理消息之间发生故障了,它将从下一消息(offset)重启,该消息将不会被处理。这可能导致重要消息丢失。
不想数据丢失的选择是至少接受一次。消费者从一个分区读取数据,先处理数据,再处理完之后再确认该消息(offset)。在这种情况下,消费者在处理和确认消息之间发生故障,再重启仍然会处理该消息。这会导致下游出现重复的消息,但不会丢失消息。
每条消息只接受一次是通过消费者处理消息,然后将消息处理的结果和offset提交到交易系统来保证的。如果消费者故障,能从上一次提交的交易恢复,并从这开始处理。这将不会发生数据的丢失和重复。然而,每次消息和偏移都当成交易进行确认会显著降低系统的吞吐量。
实际上,大部分Kafka消费者应用选择至少接受一次消息,因为提供了吞吐量与准确性之间的最佳平衡。它将重复消息的处理扔给了下游系统。
总结
Kafka有充分的理由能也正在快速地成为很多机构数据管道的基石。通过使用Kafka作为消息总线,我们能实现消息生产者和消费者之间高水平地并行和解耦,使我们的架构更灵活多变。本文只是提供了Kafka架构的鸟瞰图。更多的可参考Kafka文档。享受学习Kafka然后更多地使用它。