【读书笔记】《深入理解Kafka:核心设计与实践原理》

2021/6/13 18:52:00

本文主要是介绍【读书笔记】《深入理解Kafka:核心设计与实践原理》,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

基本概念

  • Kafka 最初有Linkedin公司开发,是一个分布式、支持多分区、多副本、多订阅者,基于ZooKeeper 的分布式消息流平台。
  • Producer 将消息发送到Broker,Broker 负责将收到的消息存储在磁盘中,而Consumer负责从Broker 订阅并消费消息。
  • Producer生产者,发送消息的一方,负责发布消息到Kafka。
  • Consumer消费者,消费消息的一方,连接到Kafka上并接收消息,并进行相应的业务逻辑处理。
  • Broker服务代理节点,可以简单的看做一个独立的Kafka服务节点或Kafka服务实例。
  • ZooKeeper 管理Kafka 集群,负责存储集群Broker、Topic、Partition等meta数据存储,同事也负责Broker故障发现,Partition Leader选举,负载均衡等功能。

在这里插入图片描述

  • Topic Kafka中的消息以主题为单位进行归类,是一个逻辑上的概念。生产者负责将消息发送到特定的主题,消费者负责订阅主题并进行消费。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上,但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)。
  • Partition分区是物理上的概念,每个Topic包含一个或多个Partition。一个分区只属于一个主题,又叫主题分区,存储层面体现为一个日志文件。Partition物理上由多个Segment 文件组成,每个Segment 大小相等,顺序读写,每个Segment数据文件以该段中最小的offset,文件扩展名为.log,生产者发送消息到partition,消费者从partition拉取消息进行消费。
  • Consumer Group 每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)。
  • offset 是消息在分区中的唯一标示,Kafka 通过它来保证消息在分区内的顺序性,但是offset 并不跨越分区(Partition),Kafka保证的是分区的有序而不是主题有序。
  • 如果分区规则设定得合理,所有的消息都可以均匀地分配到不同的分区中。如果一个主题只对应一个文件,那么这个文件所在的机器I/O将成为这个主题的性能瓶颈,而分区解决了这个问题。//一个主题对应了8个分区,部署在两地,分别4个。
  • Kafka 为分区引入了多副本(Replica)机制,通过增加副本的数量可以提升容灾的能力。同一分区的不同副本中保存的相同的消息,注意同一时刻,由于主从数据同步延迟,副本之间并非完全一致。

在这里插入图片描述

  • Consumer 使用拉(Pull)模式从服务端拉取消息,并且保存消费的具体位置,当消费者宕机后恢复上线时可以根据之前保证的消费位置重新拉取需要的消息进行消费。
  • Leader 副本负责维护和跟踪ISR(In-of-Sync Replicas)集合中所有Follower 副本的滞后状态,当Follower副本滞后太多而失效时,Leader 副本会把它从ISR 集合中剔除,转移到OSR集合中(Out-of-Sync Replicas)。如果OSR集合中Follower 副本追上了Leader 副本,那么转移到ISR集合中。注意默认情况下当Leader 副本发生故障时,只有ISR 集合中的副本才有资格被选举为新的Leader,OSR集合中副本没有机会。
  • 同步复杂要求所有能工作的Follower 副本都复制完,这条消息才会被确认为已成功提交,这种方式极大影响了性能。而在异步复制方式下,Follower 副本异步从leader副本复制数据,数据只要被leader 副本写入就被认为已经成功提交,这种情况Follower 都还没有复制完落后于Leader 副本,如果Leader 突然宕机,则会造成数据丢失。Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制,而是ISR 方式,有效地权衡数据可靠性和性能之间的关系。
  • ISR 与HW(High Watermark)和LEO(Log End Offset)有紧密的关系,HW标志了一个特定的消息偏移量,消费者只能拉取到这个offset之前的消息。LEO标示昂前日志文件中下一条待写入的消息offset,分区ISR集合中每个副本都会维护自身的LEO,而ISR集合中最小的LEO即为分区HW。

在这里插入图片描述
在这里插入图片描述

生产者

  • 生产逻辑:配置生产者客户端参数及创建相应的生产者实例 -> 构建待发送的消息 -> 发送消息 -> 关闭生产者实例 。//把大象装机冰箱步骤

在这里插入图片描述

  • 一个topic可以有若干个分区,且分区可以动态修改,但是只允许增加不允许减少,否则 Kafka 会抛出 InvalidPartitionsException 异常,因为减少分区要考虑数据保留问题。
  • 每个分区中的消息是有序的,各个分区之间的消息是无序的。新消息根据分区规则,采用追加的方式写入到对应分区日志文件的尾部。
  • key用来指定消息的键,它不仅是消息的附加信息,还可以用来计算分区号进而可以让消息发往特定的分区。消息以主题(Topic)为单位进行归类,而key可以让消息再进行二次归类,同一个key的消息会被划分到同一个分区中。

在这里插入图片描述

  • 同分区的不同副本中保存的是相同的消息(在同一时刻,副本之间并非完全一样),副本之间是“一主多从”的关系,其中leader副本负责处理读写请求, follower副本只负责与 leader副本的消息同步,不支持读写分离。
  • 副本处于不同的 broker中,当 leader副本出现故障时,从 follower副本中重新选举新的 leader副本对外提供服务。
  • acks 是生产者中非常重要的的参数,它涉及消息的可靠性和吞吐量之间的权衡。acks= 1,生产者发送消息之后,只要分区Leader副本成功写入消息,那么他就会收到来自服务端的成功响应,是消息可靠性和吞吐量之间的折中方案;acks=0,生产者发送消息之后不需要等待任何服务端的响应,优势是最大吞吐量,劣势是消息从发送到写入kafka 过程中出现某些异常,生产者无从感知,消息也就丢失了;acks=-1,生产者在消息发送之后,需要等待ISR中的所有副本都成功写入才能够收到来自服务端的成功响应,优势是可靠性很强。
  • compression参数指定消息的压缩方式,默认为none 不压缩,可以设置为“gzip”、“snappy”、“lz4”,消息压缩可以极大地减少网络传输量、降低网络I/O,从而提高整体性能。压缩是一种使用时间换空间的优化方式。
  • max.request.size用来限制生产者客户端能发送的消息最大值,默认为1M。retries配置生产者重试的次数,默认为0次。retry.backoff.ms用来设定两次重试之间的时间间隔,避免无效频繁重试。request.timeout.ms配置Producer等待响应的最长时间,默认为30000ms

消费者

  • 每一个分区只能被一个消费组中一个消费者所消费,一个消费者可以消费多个分区。如果消费者过多,出现了消费者的个数大于分区个数的情况,就会出现消费者分配不到任务分区。

在这里插入图片描述
在这里插入图片描述

  • 对于消息中间件而言,一般有两种消息投递模式:点对点(P2P)模式和发布/订阅(Pub/Sub)模式。点对点是基于队列的,发布定于模式定义了如何向一个内容节点发布和订阅,这个内容节点成为主题,主题可以认为是消息传递的中介,消息发布者将消息发布到某个主题,消息订阅者从主题中订阅消息,一对多广播时采用发布/订阅模式。
  • 如果所有的消费者都隶属于同一个消费组,那么所有的消息都会被均衡地投递给每个消费者,即每条消息只会被一个消费者处理,这就相当于点对点模式的应用。
  • 如果所有的消费者都隶属于不同的消费组,那么所有的消息都会被广播给所有的消费者,即每条消息会被所有的消费者处理,这就相当于发布/订阅模式的应用。
  • 消费步骤:创建消费者实例 -> 订阅主题 -> 拉取消息并消费 -> 提交消费位移 -> 关闭消费者实例。
  • Kafka 中消费时基于拉模式的。是补一个不断轮询的过程,消费者要做的就是不断重复地调用poll()

主题与分区

  • 主题(Topic)和分区(Partition)都是逻辑上概念,分区可以有一个至多个副本,每个副本对应一个日志文件,每个日志文件对应一至多个日志分段(LogSegment),每个日志分段还可以细分为索引文件、日志存储文件和快照文件等。

问答

消息队列适用于哪些场景?

  • 异步处理:消息发送者 可以发送一个消息而无须等待响应,耗时久的服务异步处理。
  • 应用解耦:发送者和接受者不必了解对方、只需要确认消息,比如发送和接收者可以是不同的系统,不同的语言编写的,地理上可以不在同一个地域,发送者和接受者不必同时在线。
  • 流量削峰:当在线接口在应对高峰流量时,比如“秒杀”,“流量激增”时,如果接口处理能力有限,可以先将无法及时处理的请求发送给消息队列,后台处理,防止将api接口打死。
  • Pub/Sub模型:一条消息,可以广播给任意个收听方

MQ如何保证传输可靠性?

生产者丢失

生产者丢失消息的可能点在于程序发送失败抛异常了没有重试处理,或者发送的过程成功但是过程中网络闪断MQ没收到,消息就丢失了。

  • 同步处理:如RabbitMQ提供事务功能channel.txSelect,生产者发生数据MQ没有接受到,生产者会收到报错,然后回滚事务,重新发送消息;如果收到消息则可以提交事务,缺点是开启事务后吞吐量低,耗费性能。
  • 异步处理:生产者开启confirm功能,发送的消息会分配唯一id,如果写入了MQ则会返回一个ACK,否则回调一个neck接口,告诉你这个接口调用失败,可以重试,由于内存中维护每个消息的状态,如果超过一定时间还没接收到这个消息的回调,重发消息。通常采用confirm方法。

MQ丢失

如果生产者保证消息发送到MQ,而MQ收到消息后还存在内存中,这时候宕机了又没来得及同步给其他节点就有可能导致消息丢失。

  • RocketMQ分为同步刷盘和异步刷盘两种方式,默认的是异步刷盘,就有可能导致消息还未刷到硬盘上就丢失了,可以通过设置为同步刷盘的方式来保证消息可靠性,这样即使MQ挂了,恢复的时候也可以从磁盘中去恢复消息。
  • 上面介绍Kafka的acks =-1 或者all,生产者在消息发送之后,需要等待ISR中的所有副本都成功写入才能够收到来自服务端的成功响应
  • Kafka的其他配置replication.factor=N,要求每个partition至少有2个副本;min.insync.replicas=N,Leader副本至少感知一个Follower还保持着连接;retries=N让生产者发送失败多次重试。

消费者丢失

消费端刚收到消息,此时服务器宕机,MQ认为消费者已经消费,不会重复发送消息,消息丢失。

  • RocketMQ默认是需要消费者回复ack确认的,Kafaka 需要手动开启配置关闭自动offset,确保处理完后通过API调用发送ack,如果MQ没收到ack,则重发数据。

Kafka高性能在哪些方面?

  • 消息持久化:Kafka采用顺序写的方式来做消息持久化,顺序I/O比随机I/O性能要好很多,减少了寻址耗费时间。服务端会将每条消息的顺序值转换成绝对偏移量(Broker从Partition维度来标记消息的顺序,用于控制Consumer消费消息的顺序),Kafka通过nextOffset(下一个偏移量)来记录存储在日志中最近一条消息的偏移量。
  • 批量发送消息:Kafka瓶颈通常不在于磁盘,而在于网络。Kafka通过将Topic划分成多个Partition,Producer将消息分发到多个本地Partition的消息队列(双端队列)中,每个Partition消息队列中的消息会写入到不同的Leader节点。Producer先生产消息、序列化消息并压缩消息后,追加到本地的记录收集器(RecordAccumulator),Sender不断轮询记录收集器,当满足一定条件时(消息大小达到阈值,消息等待发送时间达到阈值),将队列中的数据发送到Partition Leader节点。其中队列的每个元素是一个批记录(ProducerBatch),批记录使用createdMs表示批记录的创建时间(批记录中第一条消息加入的时间), topicPartion表示对应的Partition元数据。记录的批处理通过使用更大的数据包,以及提高带宽效率来分摊网络往返的开销。
  • 分区分段:Kafka采取了分区的模式,而每一个分区又对应到一个物理分段,而查找的时候可以根据二分查找快速定位。
  • 压缩:上文有谈到可压缩参数,一方面减少带宽,也减少数据传输的消耗。
  • 零拷贝:Linux系统提供了系统事故调用函数sendfile(),可以直接把内核缓冲区里的数据拷贝到Socket缓冲区中,不用拷贝到用户态了。因此减少CPU的上下文切换和磁盘IO,上下文切换次数由4个减少到2个,数据拷贝由5次减少到3次。(下图来自公众号“腾讯技术工程”)

在这里插入图片描述

在使用消息队列中会遇到哪些问题?

可能原因解决方案
消息积压1)生产端没有限流;2)消费端耗时久;3)消费消费端消费线程少;4)客户端在消费失败后设置CONSUME_FAILURE,一旦不能恢复会导致一直重试1)生产端增加限流;2)发现问题及时扩容Partition并扩容消费者机器;3)增加消费端线程数;4)消费失败不要使用 CONSUME_FAILURE,可以使用RECONSUME_LATER,一段时间后再进行消费,避免该消息一直处理失败
消息丢失1)数据可靠性未设置acks=-1;2)消息过大造成发送失败;3)集群机器宕机1)如果对消息丢失0容忍可设置客户端 ack=-1;2)不要发送超过1M以上消息;3)做好集群容灾处理,尽量保证partition均匀分布在所有Broker中。
重复消费生产者重复生产消息消息消费严格幂等控制

MQ技术选型

KafkaRocketMQRabbitMQActiveMQ
单机吞吐量10万级10万级万级万级
开发语言ScalaJavaErlangJava
高可用分布式架构分布式架构主从架构主动架构
性能ms级ms级us级ms级
功能功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用MQ功能较为完善,扩展性好并发性能强,延时低MQ功能完善
社区活跃度


这篇关于【读书笔记】《深入理解Kafka:核心设计与实践原理》的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程