点击上方“Python与算法社区”,选择“星标”公众号
第一时间速享原创干货
系列第三篇,总第290篇原创
1
Kafka作为一个消息系统,为什么会如此受欢迎?消息系统在不同系统传输数据中扮演着非常重要的角色。让我们看看没有消息系统的数据管道会怎样?
以Hadoop为例,Kafka起始于数据存储与处理的Hadoop开源框架。如果没有数据,Hadoop就变的一点用没有,所以首先应该是将数据迁移到Hadoop。
数据可能来源于不同的独立系统,这些系统都需要与Hadoop交互,相互交互。
这就会变的非常复杂,不同系统之间可能存在许多通信通道,不同通道之间需要自定义协议与通信方法,因此迁移数据就成了开发团队的焦点,所有精力都被集中到数据传输而不是业务实现。
在看这张图片,如果使用Kafka作为消息传输的缓冲区,所有应用程序之间的依赖都被解耦合了,只需要发送数据到Kafka,然后从Kafka取消息。应用程序只需要针对Kafka的消息传输接口,开发出一套消息生产或者消费接口即可。
2
什么是Kafka?
Kafka是一个基于发布订阅模式实现的快速、高可扩展、高可备份的分布式消息系统。
Kafka的分布式设计有以下几个优点:
(1)Kafka允许有大量的永久或者临时消费者消费消息。
(2)对于节点障碍,Kafka支持自动恢复。
这些优点使得kafka活跃在大规模数据系统之间。
3
Kafka术语
Kafka理论主要涉及以下概念:topics、producers、consumers、brokers、partitions
Kafka消息是以topic来归类集中到一起,不同topic的消息将不会被归类到一起。如果你想要发送一条消息,首先你要指定相应的topic,将消息缓存到指定的topic里。
消费者想要从Kafka拉取消息,也要指定相应的topic,获取哪个topic里的消息。Kafka作为一个分布式消息系统,是以集群的方式运行,每个节点都被称作broker。
4
Kafka topic
Topic对消息的管理分类是以partitions来实现的,partition是分区的意思,producers产生的消息都会被存储到partition中,一个topic可以有一个或者多个partition。
每个消息在partition中都会分配一个offset标识,标识这个消息在这个partition中的位置。因此一个消息可以通过(topic、partition、offset)这个元组来确定。
消息进入到partition中是有顺序的且不可变,但是可以通过offset来实现任意需求的消费消息。不一定非要从消息队列的尾部消息,也不一定是头部,有了offset你可以从任意位置,任意的方向,甚至是随机的消费消息。
5
Kafka Log
分区(partition)读写消息实际上也是一种逻辑上的实现,消息真正的读写是在Kafka Log文件系统中实现的。
如果你在主机上启动Kafka在topic为test的情况下创建两个分区,你可以进入到具体目录里面看到,有test0、与test1两个文件夹(假设没有指定副本个数),进入到test0里面,会有index与log两个文件,log文件就负责物理意义上的存储消息。
所以为什么Kakfa读写速度如此之快,其中一个原因就是按照顺序将消息存储到文件系统当中。下图是数据源往Log文件中写数据, 其他两个消费者在自定义offset的情况下消息消费数据。
也是因为如此,Kafka的消息不会因为被消费了而消失,其他的消费者在指定offset的情况下也能够消费到已经被其他消费者消费过的消息。
Kakfa可以自定义配置有效消息时间周期,也就是说会按期清理过期的消息。
如果有效消息周期设置为1天,消费者不能够在一天内消费完,那么消息将会丢失,如果在消费过程中节点挂掉,恢复后又会重新消费。从Kafka的角度来看,它没有对消费者从主题中消费的消息保持任何状态。
6
Partitions and Brokers
每个broker是一个运行的kafka实例,每个broker下可以有多个partition,一个topic可以由多个broker来维护,因此可以得知就概念的范围来说:topic>broker>partition
Kakfa为了保障数据的安全性,也有副本机制。创建topic时候一般会指定分区个数与副本个数,如下图,假设启动3个broker指定3个分区分别是partition0、partition1、partition2,和2个副本。
Kafka中每个partition都会有一个leader负责读写数据,另外两个作为副本,只备份leader中的数据。这里leader负责了两个功能,一是负责响应producer与consumer,也就是读写消息。另外负责把新的消息协调更新到副本。
假设客户端往partition0中写消息,如下图,leader响应后,将消息存储到broker1的partition0中,然后再往broker2、broker3中的partiton0中备份消息。
7
消费者与消费者组
Kafka是以组来归类消费者,因为Kafka是基于发布订阅模式设计的,如果生产一条消息,那么所有订阅相应topic的consumer都会收到这条消息,如果想要所有consumer只能其中一个来消费,那么就把它们都设置为同一组,这样consumer就会排队消费message,如果想要所有的consumer都收到消息(比如说新闻消息)那么把所有的consumer都设置为不同的组即可。
Kafka为什么会有较高的吞吐量呢?假设把consumers归为一组,那么consumer只会从topic下的其中一个partition去拉取消息,其他partition不管,这样就保证了在多个consumers条件下每条消息只会被消费一次。
这样就会存在这样一种现象,如果consumers的数量大于partitions,则有部分consumer将会处于闲置状态,如果小于的话,则部分partition不会被消费,这将由kafka的负载均衡机制来解决,避免partition之间messages数量差异过大。
如果consumers等于patitions的数量,那么每个partition都会分配一个consumer并行消息,这样就造成了Kafka有较高的吞吐量。笔者之前也设计过相关的程序,采取的是消息队列的方式,多个consumers从一个队列中拉取messages,这个弊端就是在一定情境下,无论我启动多少个consumers,只有固定数量的consumer在工作,而其他的处于闲置状态。
这张图来自kafka官方文档,图中有两个server,分别是server1、server2。有两个消费者组,groupA与groupB。图中,C1与C2向Server1拉取messages,C2向Server2拉取。GroupB中,每个consumer都分配了一个partition。
8
Kafka写操作
Kafka写操作主要由leader完成,leader要将消息写入到partition中,并且还要写到其他replica(副本)中。
一共有三种形式来完成Kafka写操作
producer 产生一条消息,发送给leader,leader写入到partition中,然后询问replica你们是否alive?如果得到ack则写到replica中,replica在返回一个ack告诉leader写完了,然后leader再把ack返回给producer,告诉它我完成了写操作。
leader直接将message写到partition中,得到replica的alive响应后,直接将message写到replica的partition中不等待replica的写完ack直接响应producer告诉它我完成了写操作
producer直接将message扔给leader,不等待任何响应就继续下一条message生产。
如果某个replica发生故障呢?
Partition之间都有一个心跳机制,leader通过心跳获取follower的状态,如果一段时间内没有得到follower的响应,则认为死亡就不会把消息备份到这个replica中。
如果leader发生故障,则在其他replica中继续选举一个leader。有趣的是kafka的选举机制依赖于zookeeper,通过zookeeper的序列节点来完成,每启动一个partition, 就会在zookeeper中注册一个序列节点(如果忘记序列节点特性的话,可以去看看上一节中zookeeper的介绍)。哪个节点的编号最小,则为leader。如果leader故障,选次之作为leader。
9
Kafka的理论就介绍到这,如还有没覆盖到的欢迎提出一起学习,笔者作为Kafka小白总结本篇文章主要参考https://sookocheff.com/post/kafka/kafka-in-a-nutshell/。
下篇文章将会介绍如何搭建分布式Kafka集群,以及Kafka Stream介绍。
推荐阅读
点个在看
支持原创!