【.com原创稿件】本文主要讲解什么是Kafka,Kafka的架构包括工作流和存储机制,以及生产者和消费者。图片来自Pexels最后大家会掌握Kafka中最重要的几个概念,分别是Broker、Producer、Consumer、ConsumerGroup、Topic、Partition、Replica、Leader、Follower。这是学习和理解Kafka的基础和必备内容。定义Kafka是一个分布式的基于发布/订阅的消息队列(MessageQueue),主要应用于大数据的实时处理领域。消息队列Kafka本质上就是一个MQ(MessageQueue)。使用消息队列有什么好处?(面试题)解耦:让我们可以独立扩展或者修改队列两边的处理。可恢复性:即使一个消息处理进程挂了,加入队列的消息在系统恢复后仍然可以继续处理。缓冲:有助于解决生产消息和消费消息处理速度不一致的情况。弹性&峰值处理能力:不会因为突发的过载请求而彻底崩溃,消息队列可以让关键组件承受突发的访问压力。异步通信:消息队列允许用户将消息放入队列而不立即处理。发布/订阅模型是一对多的。生产者向主题发布消息,多个消费者订阅主题。发布到主题的消息将被所有订阅者消费,消费的数据不会立即从主题中清除。架构Kafka存储来自任意数量称为生产者的进程的消息。从而可以将数据发布到不同Topic主题下的不同Partition分区中。在分区内,这些消息被索引并与时间戳一起存储。其他称为消费者的进程可以订阅来自分区的消息。Kafka运行在由一台或多台服务器组成的集群上,分区可以跨集群节点分布。下面给出Kafka的一些重要概念,让大家对Kafka有一个整体的认识和认知。后面会详细分析各个概念的作用和更深层次的原理:Producer:消息生产者,向KafkaBroker端发送消息的客户端。Consumer:消息消费者,从KafkaBroker中获取消息的客户端。ConsumerGroup:ConsumerGroup(CG),消费组中的每个消费者负责消费不同分区的数据,提高消费能力。一个partition只能被group中的一个consumer消费,consumergroup之间互不影响。所有的消费者都属于一个消费者组,即一个消费者组是一个逻辑上的订阅者。Broker:Kafka机器就是Broker。一个集群由多个Broker组成。一个Broker可以容纳多个Topic。Topic:可以理解为队列。主题对消息进行分类。生产者和消费者面对同一个话题。Partition:为了实现可扩展性和提高并发性,可以将一个非常大的Topic分布到多个Broker(服务器)上,一个Topic可以划分为多个Partition,每个Partition是一个有序队列。Replica:为了实现备份功能,保证当集群中某个节点发生故障时,该节点上的Partition数据不会丢失,Kafka可以继续工作。Kafka提供了副本机制。每个partition的一个Topic有几个副本,一个Leader和几个Follower。Leader:每个分区的多个副本的“主”副本,生产者发送数据的对象,消费者消费数据的对象都是领导者。Follower:每个分区的多个副本的“从”副本,实时从Leader同步数据,并与Leader数据保持同步。当Leader失效时,一个Follower也会成为新的Leader。Offset:consumer消费的位置信息,监听数据消费到哪里,当consumer挂掉再恢复时,可以从消费位置继续消费。Zookeeper:Kafka集群要正常工作,需要依赖Zookeeper,它帮助Kafka存储和管理集群信息。工作流Kafka集群将记录流存储在称为主题的类别中,每条记录都包含一个键、一个值和一个时间戳。Kafka是一个分布式流媒体平台,到底是什么意思呢?发布和订阅记录流,类似于消息队列或企业消息系统。以容错、持久的方式存储记录流。处理记录流。Kafka中的消息按主题分类。生产者生产消息,消费者消费消息,都是为了同一个主题。Topic是逻辑概念,Partition是物理概念。每个Partition对应一个log文件,里面存放的是Producer产生的数据。Producer产生的数据会不断的追加到日志文件的末尾,每条数据都有自己的Offset。consumergroup中的每个consumer都会实时记录自己消费了哪个Offset,这样当错误恢复的时候,可以从上次的位置继续消费。存储机制由于生产者产生的消息会不断的追加到日志文件的末尾,为了防止日志文件过大导致数据定位效率低下,Kafka采用了分片和索引机制。它将每个Partition分成多个Segment,每个Segment对应两个文件:“.index”索引文件和“.log”数据文件。这些文件位于同一个文件下,该文件夹的命名规则为:主题名-分区号。例如topicfirst有3个分区,对应的文件夹为first-0、first-1、first-2。#ls/root/data/kafka/first-00000000000000009014.index00000000000000009014.log0000000000000009014.timeindex0000000000000009014.snapshotleader-epoch-checkpoint文件的第一条消息命名为当前的SegmentOgmentOgmentOgmentOgmentOgmentOg下图是索引文件和日志文件的结构示意图:“.index”文件存放大量索引信息,“.log”文件存放大量数据,索引文件中的元数据指向到相应数据文件中Message的物理偏移量。ProducerPartitionStrategy分区原因:方便在集群中扩展。每个Partition都可以根据所在的机器进行调整,一个Topic可以由多个Partition组成,因此可以以Partition为单位进行读写。可以提高并发性,所以可以以Partition为单位进行读写。分区原则:我们需要将Producer发送过来的数据封装到一个ProducerRecord对象中。这个对象需要指定一些参数:topic:字符串类型,NotNull。分区:int类型,可选。时间戳:long类型,可选。key:字符串类型,可选。value:字符串类型,可选。headers:数组类型,可为空。①指定Partition时,直接使用给定的Value作为Partition的值。②如果没有指定Partition但有Key,则Key的Hash值与分区数取余得到Partition值。③在既没有Partition也没有Key的情况下,第一次调用时随机生成一个整数(此整数会在后续每次调用时递增),取这个值和可用分区数的余数,得到Partition值.这就是所谓的Round-Robin轮询算法。数据可靠性保证为保证Producer发送的数据能够可靠地发送到指定的Topic,Topic的每个Partition接收到Producer发送的数据后,需要向Producer发送ACK(ACKnowledgetoconfirmreceipt).如果Producer收到ACK,则发送下一轮,否则重新发送数据。①复制数据同步策略什么时候发送ACK?确保Follower和Leader是同步的,Leader再次发送ACK,保证Leader挂掉后,可以在Follower中选举出新的Leader,不会丢失数据。同步完成后有多少Follower发送ACK?所有Follower同步,然后发送ACK。②ISR采用第二种方案。所有Follower都同步完成后,Producer就可以继续发送数据了。如果某个Follower由于某种原因失败,Leader将不得不等待它完成同步。如何解决这个问题呢?Leader维护一个动态同步副本集(ISR):一组与Leader同步的Follower。当ISR集中的Follower完成数据同步后,Leader会向Follower发送ACK。如果Follower长时间不与Leader同步数据,Follower将被踢出ISR集合,时间阈值由replica.lag.time.max.ms参数设置。leader失效后,将从ISR中选举出新的leader。③ACK响应机制对于一些不重要的数据可靠性要求不是很高,可以容忍少量的数据丢失,所以不需要等待ISR中的所有Follower都接受成功。因此,Kafka为用户提供了三个可靠性级别。用户可根据可靠性和时延要求选择以下配置。ack参数配置:0:Producer不等待Broker的ACK,延迟最低。Broker一收到数据就返回,还没有写入磁盘。当Broker发生故障时,数据可能会丢失。1:Producer等待Broker的ACK,Partition的Leader放置成功后返回ACK。如果Leader在Follower同步成功之前就挂掉了,数据就会丢失。-1(all):Producer等待Broker的ACK,Partition的Leader和Follower都下单成功才返回ACK。但是当Broker发送ACK时,Leader失效,会造成数据重复。④故障处理细节LEO:每个副本的最大Offset。HW:消费者能看到的最大Offset,ISR队列中最小的LEO。Followerfailure:一个follower失败后,会被暂时踢出ISR集合。follower恢复后,follower会读取本地磁盘上记录的最后一个HW,截取日志文件中高于HW的部分。Leader执行同步数据操作。Follower的LEO大于等于Partition的HW后,即Follower追上Leader后,可以重新加入ISR。Leader失效:Leader失效后,会从ISR中选出新的Leader。之后,为了保证多副本之间的数据一致性,剩下的follower会先切掉自己日志文件中高于HW的部分,再从新的Leader上同步数据。注意:这里只保证副本之间的数据一致性,不保证数据不丢失、不重复。ExactlyOnce语义将服务器的ACK级别设置为-1,可以保证Producer和Server之间不会丢失数据,即AtLeastOnce语义。相比之下,将服务器ACK级别设置为0可以保证生产者的每条消息只会发送一次,即AtMostOnce语义。AtLeastOnce可以保证数据不丢失,但不能保证数据不重复;相比之下,AtMostOnce可以保证数据不会重复,但不能保证数据不会丢失。但是,对于一些非常重要的信息,比如交易数据,下游的数据消费者要求数据既不重复也不丢失,即ExactlyOnce语义。Kafka0.11版本引入了幂等性:无论Producer向Server发送多少重复数据,Server只会持久化一份数据。即:AtLeastOnce+idempotency=ExactlyOnce开启幂等性只需要将Producer参数中的enable.idompotence设置为true即可。启用幂等性的Producer将在初始化期间分配一个PID,发送到同一Partition的消息将附带一个SequenceNumber。而在Borker这边,重启后PID会发生变化,不同的Partition也有不同的主键,因此幂等性无法保证跨分区会话的ExactlyOnce。Consumer消费方式Consumer采用Pull(拉取)方式从Broker中读取数据。Consumer采用Push(推送)方式,Broker向Consumer推送消息的速率由Broker决定,难以适应不同消费速率的消费者。它的目标是尽可能快地传递消息,但这很容易导致消费者来不及处理消息,典型的表现是拒绝服务和网络拥塞。Pull模式可以根据消费者的消费能力,以合适的速率消费消息。Pull模式的缺点是如果Kafka没有数据,消费者可能会陷入循环,一直返回空数据。因为consumer主动从Broker拉取数据,所以需要维护一个longpoll。为此,Kafka消费者在消费数据时会传入一个duration参数timeout。如果当前没有可供消费的数据,Consumer会等待一段时间再返回。这段时间超时。Partition分配策略一个ConsumerGroup有多个Consumer,一个Topic有多个Partition,那么必然会涉及到Partition的分配,即决定哪个Partition被哪个Consumer消费。Kafka有两种分配策略,一种是RoundRobin,一种是Range,默认是Range。当消费者组中的消费者发生变化时,会触发分区分配策略(方法重分配)。①RoundRobinRoundRobin轮询方式将所有分区作为一个整体进行Hash排序。一个consumergroup分配的partition数量最大相差1,按group划分,可以解决多个consumer消费数据不均衡的问题。但是,当消费组中订阅了不同的主题时,可能会造成消费混乱。如下图,Consumer0订阅了topicA,Consumer1订阅了topicB,将topicA和B的分区排序后,分配给consumergroup,TopicB分区中的数据可能分配给Consumer0。②RangeRange方法是按照主题划分的,轮询方法不会出现消费混乱。但是如下图,Consumer0和Consumer1同时订阅了主题A和B,可能会造成消息分布不均。消费者组中订阅的主题越多,分区分布可能越不平衡。Offset的维护由于Consumer在消费过程中可能会出现断电、宕机等故障,Consumer恢复后,需要从故障前的位置继续消费。所以消费者需要实时记录自己消费了哪个offset,以便故障恢复后可以继续消费。在Kafka0.9版本之前,Consumer默认将Offset保存在Zookeeper中。从0.9版本开始,Consumer默认将Offset保存在一个内置的KafkaTopic中,即__consumer_offsets。综上所述,上面和大家深入探讨了Kafka的架构,更侧重于理论和基础。这是掌握Kafka必备的内容。接下来,我将以代码和示例的形式更新Kafka的API和高级事务、拦截器、监控。这篇文章,让大家全面了解并知道如何使用Kafka。作者:臧元辉简介:就职于中科星途股份有限公司(北京),研发部后端技术组。个人擅长Python/Java开发,了解前端基础知识;精通MySQL、MongoDB、Redis;熟悉Linux开发环境,掌握Shell编程,有良好的Git源码管理习惯;精通Nginx、Flask、Swagger开发框架;有Docker+Kubernetes云服务开发经验。他对人工智能和云原生技术有更大的兴趣。【原创稿件,合作网站转载请注明原作者和出处为.com】
