第1章 初识 Kafka

Kafka 架构有什么组件?

一个典型的 Kafka 体系架构包括若干 Producer、若干 Broker 、若干 Consumer,以及一个 ZooKeeper 集群。

ZooKeeper 有什么作用?

ZooKeeper 是 Kafka 用来负责集群元数据的管理、控制器的选举等操作的。

Broker 是什么?

服务代理节点。对于 Kafka 而言,Broker 可以简单地看作一个独立的 Kafka 服务节点或 Kafka 服务实例。大多数情况下也可以将 Broker 看作一台 Kafka 服务器,前提是这台服务器上只部署了一个 Kafka 实例。一个或多个Broker 组成了一个 Kafka 集群。一般而言,我们更习惯使用首字母小写的 broker 来表示服务代理节点。

什么是主题?

Kafka 中的消息以主题为单位进行归类,主题是一个逻辑上的概念,它还可以细分为多个分区

什么是分区(partition)?

kafka 中,topic 是消息的归类,一个 topic 可以有多个分区,每个分区保存部分 topic 的数据,所有 partition 当中的数据全部合并起来,就是一个 topic 当中的所有数据。

一个 broker 服务下,可以创建多个分区,broker 数与分区数没有关系。

Kafka中的分区可以分布在不同的服务器(broker)上,也就是说,一个主题可以横跨多个broker,以此来提供比单个 broker 更强大的性能。

Kafka 为什么要设计分区这个概念呢?

如果一个主题只对应一个文件,那么这个文件所在的机器 I/O 将会成为这个主题的性能瓶颈,而分区解决了这个问题。每一条消息被发送到 broker 之前,会根据分区规则选择存储到哪个具体分区。如果分区规则设定得合理,所有的消息都可以均匀分配到不同分区,减轻机器的 I/O 负担

(Replica)多副本机制是什么?

Kafka 为分区引入了多副本(Replica)机制,通过增加副本数量可以提升容灾能力。同一分区的不同副本中保存的是相同的消息(在同一时刻,副本之间并非完全一样),副本之间是 “一主多从” 的关系,其中 leader 副本负责处理读写请求,follower 副本只负责与 leader 副本的消息同步,很多时候 follow 副本中的消息相对 leader 副本而言会有一定的滞后。副本处于不同的 broker 中,当 leader 副本出现故障时,从 follower 副本中重新选举新的 leader 副本对外提供服务。Kafka 通过多副本机制实现了故障的自动转移,当 Kafka 集群中某个 broker 失效时仍然能保证服务可用。

Kafka 通过什么模式从服务端拉取消息?

Consumer 使用拉(Pull)模式从服务端拉取消息,并且保存消费的具体位置,当消费者宕机后恢复上线时可以根据之前保存的消费位置重新拉取需要的消息进行消费,这样就不会造成消息丢失。

Kafka 中的 ISR、AR 又代表什么? ISR 的伸缩又指什么

AR: 分区中所有的副本统称为 AR
ISR: 所有与 leader 副本保持一定程度同步的副本(包括 leader 副本)被称为 ISR (in-sync replicas),ISR 集合是 AR 集合的子集。

leader 副本负责维护和跟踪 ISR 集合中所有 follower 副本的滞后状态,当 follower 副本落后太多或失效时,leader 副本会把它从 ISR 集合中剔除。如果 OSR 集合中有 follower 副本“追上”了 leader 副本,那么 leader 副本会把它从 OSR 集合转移至 ISR 集合。默认情况下,当 leader 副本发生故障时,只有在 ISR 集合中的副本才有资格被选举为新的 leader,而在 OSR 集合中的副本则没有任何机会

replica.lag.time.max.ms : 这个参数的含义是 Follower 副本能够落后 Leader 副本的最长时间间隔,当前默认值是 10 秒。

unclean.leader.election.enable:是否允许 Unclean 领导者选举。开启 Unclean 领导者选举可能会造成数据丢失,但好处是,它使得分区 Leader 副本一直存在,不至于停止对外提供服务,因此提升了高可用性。

Kafka 中的 HW、LEO、LSO、LW 等分别代表什么?

HW是High Watermark的缩写,俗称高水位,它标识了一个特定的消息偏移量(offset),消费者只能拉取到这个offset之前的消息。

如图 1-4 所示,它代表一个日志文件,这个日志文件中有 9 条消息,第一条消息的 offset(LogStartOffset)为0,最后一条消息的 offset 为8,offset 为 9 的消息用虚线框表示,代表下一条待写入的消息。日志文件的 HW 为 6,表示消费者只能拉取到 offset 在 0 至 5 之间的消息,而 offset 为 6 的消息对消费者而言是不可见的。

LSO是LogStartOffset,一般情况下,日志文件的起始偏移量 logStartOffset 等于第一个日志分段的 baseOffset,但这并不是绝对的,logStartOffset 的值可以通过 DeleteRecordsRequest 请求(比如使用 KafkaAdminClient 的 deleteRecords()方法、使用 kafka-delete-records.sh 脚本、日志的清理和截断等操作进行修改。

LEO 是Log End Offset 的缩写,它标识当前日志文件中下一条待写入消息的 offset,图1-4中 offset 为 9 的位置即为当前日志文件的 LEO,LEO 的大小相当于当前日志分区中最后一条消息的 offset 值加 1。分区 ISR 集合中的每个副本都会维护自身的 LEO,而 ISR 集合中最小的 LEO 即为分区的 HW,对消费者而言只能消费 HW 之前的消息。

Kafka 副本间的复制机制是同步复制还是异步复制?

Kafka 的复制机制既不是完全的同步复制,也不是单纯的异步复制。事实上,同步复制要求所有能工作的 follower 副本都复制完,这条消息才会被确认为已成功提交,这种复制方式极大地影响了性能。而在异步复制方式下,follower 副本异步地从 leader 副本中复制数据,数据只要被 leader 副本写入就被认为已经成功提交。在这种情况下,如果 follower 副本都还没有复制完而落后于 leader 副本,突然 leader 副本宕机,则会造成数据丢失。Kafka 使用的这种 ISR 的方式则有效地权衡了数据可靠性和性能之间的关系。

Kafka 中是怎么体现消息顺序性的?

可以通过 分区策略 体现消息顺序性。
分区策略有轮询策略、随机策略、按消息键保序策略。

按消息键保序策略:一旦消息被定义了 Key,那么你就可以保证同一个 Key 的所有消息都进入到相同的分区里面,由于每个分区下的消息处理都是有顺序的,故这个策略被称为按消息键保序策略

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return Math.abs(key.hashCode()) % partitions.size();

如果有多个服务实例消费同一个主题,是否能保证消息顺序?

使用 kafka 只能保证相对顺序性,即分区上的顺序性,而保证不了全局顺序性,除非牺牲吞吐率一个 topic 只配一个分区

同组消费协调可以保证一个分区在一个时间段内只能被一个消费者消费,然后 每个消费者消费消息以后得提交offset 所以在rebalance的情况下,需要预防重复消费

Q:多个消费者不是为了提高吞吐吗? 那一个时间只能一个消费,那为什么还要多个
A:保证有序才这么做而已 要吞吐的是多消费者多分区
用我司的来举个例子就是只能保证一个 action 上的数据是顺序的,但多个 action 被分配到不同分区时,有可能后提交的比先提交的要先被消费到。所以要写一个自定义分区器协调不同分区的数据

第2章 生产者

Kafka 中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?

  • 分区器:分区器的作用就是为消息分配分区。如果消息中没有指定 partition 字段,那么就需要依赖分区器,根据 key 这个字段来计算 partition 的值
  • 序列化器: 生产者需要用序列化器(Serializer)把对象转换成字节数组才能通过网络发送给 Kafka。而在对侧,消费者需要用反序列化器(Deserializer)把从 Kafka 中收到的字节数组转换成相应的对象。
  • 拦截器:kafka 一共有两种拦截器:生产者拦截器和消费者拦截器
    • 生产者拦截器既可以用来在消息发送前做一些准备工作,比如按照某个规则过滤不符合要求的消息、修改消息的内容等,也可以用来在发送回调逻辑前做一些定制化的需求,比如统计类工作
    • 消费者拦截器主要在消费到消息或在提交位移时进行一些定制化操作

消息在通过 send() 方法发往 broker 的过程中,有可能需要经过拦截器(Interceptor)、序列化器(Serializer)和分区器(Partitioner)的一系列作用之后才能被真正地发往 broker。拦截器一般不是必需的,而序列化器是必需的。消息经过序列化之后就需要确定它发往的分区,如果消息 ProducerRecord 中指定了 partition 字段,那么就不需要分区器的作用,因为 partition 代表的就是所要发往的分区号。

处理顺序: 拦截器 -> 序列化器 -> 分区器

Kafka生产者客户端的整体结构是什么样子的?

整个生产者客户端由两个线程协调运行,这两个线程分别为主线程Sender 线程
在主线程中由 KafkaProducer 创建消息,然后通过可能的拦截器、序列化器、分区器的作用之后缓存到消息累加器中。
Sender 线程负责从消息累加器中获取消息并将其发送到 Kafka 中
消息累加器 (RecordAccumulator) 主要用来缓存消息以便 Sender 线程可以批量发送,进而减少网络传输的资源消耗以提高性能

Kafka生产者客户端中使用了几个线程来处理?分别是什么?

整个生产者客户端由两个线程协调运行,这两个线程分别为主线程和 Sender 线程

第3章 消费者

3.1 消费者与消费组

每个消费者只能消费所分配分区中的消息。换言之,每一个分区只能被一个消费组中的一个消费者所消费。如图

Kafka的旧版Scala的消费者客户端的设计有什么缺陷

老版本的 消费组 (Consumer Group) 把位移保存在 ZooKeeper 中。Apache ZooKeeper 是一个分布式的协调服务框架,Kafka 重度依赖它实现各种各样的协调管理。将位移保存在 ZooKeeper 外部系统的做法,最显而易见的好处就是减少了 Kafka Broker 端的状态保存开销。

ZooKeeper 这类元框架其实并不适合进行频繁的写更新,而 Consumer Group 的位移更新却是一个非常频繁的操作。这种大吞吐量的写操作会极大地拖慢 ZooKeeper 集群的性能

消费组中的消费者个数如果超过topic的分区,那么就会有消费者消费不到数据”这句话是否正确?如果正确,那么有没有什么hack的手段?

一般来说如果消费者过多,出现了消费者的个数大于分区个数的情况,就会有消费者分配不到任何分区。
开发者可以继承 AbstractPartitionAssignor 实现自定义消费策略,从而实现同一消费组内的任意消费者都可以消费订阅主题的所有分区:

public class BroadcastAssignor extends AbstractPartitionAssignor{@Overridepublic String name() {return "broadcast";}private Map<String, List<String>> consumersPerTopic(Map<String, Subscription> consumerMetadata) {(具体实现请参考RandomAssignor中的consumersPerTopic()方法)}@Overridepublic Map<String, List<TopicPartition>> assign(Map<String, Integer> partitionsPerTopic,Map<String, Subscription> subscriptions) {Map<String, List<String>> consumersPerTopic =consumersPerTopic(subscriptions);Map<String, List<TopicPartition>> assignment = new HashMap<>();//Java8subscriptions.keySet().forEach(memberId ->assignment.put(memberId, new ArrayList<>()));//针对每一个主题,为每一个订阅的消费者分配所有的分区consumersPerTopic.entrySet().forEach(topicEntry->{String topic = topicEntry.getKey();List<String> members = topicEntry.getValue();Integer numPartitionsForTopic = partitionsPerTopic.get(topic);if (numPartitionsForTopic == null || members.isEmpty())return;List<TopicPartition> partitions = AbstractPartitionAssignor.partitions(topic, numPartitionsForTopic);if (!partitions.isEmpty()) {members.forEach(memberId ->assignment.get(memberId).addAll(partitions));}});return assignment;}
}

消费者提交消费位移时提交的是当前消费到的最新消息的 offset 还是 offset+1 ?

在旧消费者客户端中,消费位移是存储在 ZooKeeper 中的。而在新消费者客户端中,消费位移存储在 Kafka 内部的主题__consumer_offsets 中。
当前消费者需要提交的消费位移是 offset+1

有哪些情形会造成重复消费?

  • Rebalance: 一个consumer正在消费一个分区的一条消息,还没有消费完,发生了rebalance(加入了一个consumer),从而导致这条消息没有消费成功,rebalance后,另一个consumer又把这条消息消费一遍。

  • 消费者端手动提交: 如果先消费消息,再更新offset位置,导致消息重复消费。

  • 消费者端自动提交: 设置offset为自动提交,关闭kafka时,如果在close之前,调用 consumer.unsubscribe() 则有可能部分offset没提交,下次重启会重复消费。

  • 生产者端: 生产者因为业务问题导致的宕机,在重启之后可能数据会重发

哪些情景下会造成消息漏消费?

1.自动提交
设置offset为自动定时提交,当offset被自动定时提交时,数据还在内存中未处理,此时刚好把线程kill掉,那么offset已经提交,但是数据未处理,导致这部分内存中的数据丢失。
2.生产者发送消息
发送消息设置的是fire-and-forget(发后即忘),它只管往 Kafka 中发送消息而并不关心消息是否正确到达。不过在某些时候(比如发生不可重试异常时)会造成消息的丢失。这种发送方式的性能最高,可靠性也最差。
3.消费者端
先提交位移,但是消息还没消费完就宕机了,造成了消息没有被消费。自动位移提交同理
4.acks没有设置为all
如果在broker还没把消息同步到其他broker的时候宕机了,那么消息将会丢失

KafkaConsumer 是非线程安全的,那么怎么样实现多线程消费?

1.线程封闭,即为每个线程实例化一个 KafkaConsumer 对象

一个线程对应一个 KafkaConsumer 实例,我们可以称之为消费线程。一个消费线程可以消费一个或多个分区中的消息,所有的消费线程都隶属于同一个消费组。

2.消费者程序使用单或多线程获取消息,同时创建多个消费线程执行消息处理逻辑。
获取消息的线程可以是一个,也可以是多个,每个线程维护专属的 KafkaConsumer 实例,处理消息则交由特定的线程池来做,从而实现消息获取与消息处理的真正解耦。具体架构如下图所示:


简述消费者与消费组之间的关系

  • Consumer Group 下可以有一个或多个 Consumer 实例。这里的实例可以是一个单独的进程,也可以是同一进程下的线程。在实际场景中,使用进程更为常见一些。
  • Group ID 是一个字符串,在一个 Kafka 集群中,它标识唯一的一个 Consumer Group。
  • Consumer Group 下所有实例订阅的主题的单个分区,只能分配给组内的某个 Consumer 实例消费。这个分区当然也可以被其他的 Group 消费。

当你使用kafka-topics.sh创建(删除)了一个topic之后,Kafka背后会执行什么逻辑?

在执行完脚本之后,Kafka 会在 log.dir 或 log.dirs 参数所配置的目录下创建相应的主题分区,默认情况下这个目录为/tmp/kafka-logs/。

在 ZooKeeper 的/brokers/topics/目录下创建一个同名的实节点,该节点中记录了该主题的分区副本分配方案。示例如下:

[zk: localhost:2181/kafka(CONNECTED) 2] get /brokers/topics/topic-create
{"version":1,"partitions":{"2":[1,2],"1":[0,1],"3":[2,1],"0":[2,0]}}

topic 的分区数可不可以增加?如果可以怎么增加?如果不可以,那又是为什么

可以增加,使用 kafka-topics 脚本,结合 --alter 参数来增加某个主题的分区数,命令如下:

bin/kafka-topics.sh --bootstrap-server broker_host:port --alter --topic <topic_name> --partitions <新分区数>

当分区数增加时,就会触发订阅该主题的所有 Group 开启 Rebalance。
首先,Rebalance 过程对 Consumer Group 消费过程有极大的影响。在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 完成。这是 Rebalance 为人诟病的一个方面。
其次,目前 Rebalance 的设计是所有 Consumer 实例共同参与,全部重新分配所有分区。其实更高效的做法是尽量减少分配方案的变动。
最后,Rebalance 实在是太慢了。

topic的分区数可不可以减少?如果可以怎么减少?如果不可以,那又是为什么?

不支持,因为删除的分区中的消息不好处理。如果直接存储到现有分区的尾部,消息的时间戳就不会递增,如此对于 Spark、Flink 这类需要消息时间戳(事件时间)的组件将会受到影响;如果分散插入现有的分区,那么在消息量很大的时候,内部的数据复制会占用很大的资源,而且在复制期间,此主题的可用性又如何得到保障?与此同时,顺序性问题、事务性问题,以及分区和副本的状态机切换问题都是不得不面对的。

创建topic时如何选择合适的分区数?

在 Kafka 中,性能与分区数有着必然的关系,在设定分区数时一般也需要考虑性能的因素。对不同的硬件而言,其对应的性能也会不太一样。
可以使用Kafka 本身提供的用于生产者性能测试的 kafka-producer- perf-test.sh 和用于消费者性能测试的 kafka-consumer-perf-test.sh来进行测试。
增加合适的分区数可以在一定程度上提升整体吞吐量,但超过对应的阈值之后吞吐量不升反降。如果应用对吞吐量有一定程度上的要求,则建议在投入生产环境之前对同款硬件资源做一个完备的吞吐量相关的测试,以找到合适的分区数阈值区间。
分区数的多少还会影响系统的可用性。如果分区数非常多,如果集群中的某个 broker 节点宕机,那么就会有大量的分区需要同时进行 leader 角色切换,这个切换的过程会耗费一笔可观的时间,并且在这个时间窗口内这些分区也会变得不可用。
分区数越多也会让 Kafka 的正常启动和关闭的耗时变得越长,与此同时,主题的分区数越多不仅会增加日志清理的耗时,而且在被删除时也会耗费更多的时间。

深入理解 Kafka :核心设计与实践 读书笔记相关推荐

  1. 《深入理解Kafka:核心设计与实践原理》笔误及改进记录

    2019年2月下旬笔者的有一本新书--<深入理解Kafka:核心设计与实践原理>上架,延续上一本<RabbitMQ实战指南>的惯例,本篇博文用来记录现在发现的一些笔误,一是给购 ...

  2. 深入理解Kafka核心设计与实践原理_01

    深入理解Kafka核心设计与实践原理_01 01_初识Kafka 1.1 基本概念 1.2 安装与配置 1.3 生产与消费 1.4 服务端参数配置 01_初识Kafka 1.1 基本概念 一个典型的 ...

  3. Kafka核心设计与实践原理总结:进阶篇

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! kafka作为当前热门的分布式消息队列,具有高性能.持久化.多副本备份.横向扩展能力.我学习了<深入理解K ...

  4. Kafka核心设计与实践原理总结:基础篇

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! 一.基本概念 1.体系架构 Producer:生产者 Consumber:消费者 Broker:服务代理节点(k ...

  5. 新书《深入理解Kafka:核心设计与实践原理》上架,感谢支持~

    新书上架 初识 Kafka 时,笔者接触的还是 0.8.1 版本,Kafka 发展到目前的 2.x 版本,笔者也见证了Kafka的蜕变,比如旧版客户端的淘汰.新版客户端的设计.Kafka 控制器的迭代 ...

  6. 推荐系统实践读书笔记-02利用用户行为数据

    推荐系统实践读书笔记-02利用用户行为数据 为了让推荐结果符合用户口味,我们需要深入了解用户.如何才能了解一个人呢?<论语·公冶长>中说"听其言,观其行",也就是说可以 ...

  7. 推荐系统实践读书笔记-04利用用户标签数据

    推荐系统实践读书笔记-04利用用户标签数据 推荐系统的目的是联系用户的兴趣和物品,这种联系需要依赖不同的媒介.GroupLens在一篇文章中表示目前流行的推荐系统基本上通过3种方式联系用户兴趣和物品. ...

  8. 推荐系统实践读书笔记-01好的推荐系统

    推荐系统实践读书笔记-01好的推荐系统 在研究如何设计推荐系统前,了解什么是好的推荐系统至关重要.只有了解了优秀推荐系统的特征,我们才能在设计推荐系统时根据实际情况进行取舍.本章分3个步骤来回答这个问 ...

  9. 推荐系统实践读书笔记-08评分预测问题

    推荐系统实践读书笔记-08评分预测问题 本书到目前为止都是在讨论TopN推荐,即给定一个用户,如何给他生成一个长度为N的推荐列表,使该推荐列表能够尽量满足用户的兴趣和需求.本书之所以如此重视TopN推 ...

最新文章

  1. react native 之自定义顶部导航栏,实现标题居中可控
  2. MySQL数据库学习【第九篇】索引原理与慢查询优化
  3. Singleton模式学习
  4. [ARM异常]-armv8-aarch64下当中断来时自动触发的硬件行为
  5. 消费扶贫谋定中国农民丰收节交易会 洛水山肴乡村振兴
  6. java part.inlimen_java字符串加密及动手动脑
  7. 使用request获取访问者的真实IP
  8. Xcode 8.1 : Unable to read from device
  9. 关于配置了数据库方言为MySQLInnoDBDialect后Hibernate不能自动建表的问题
  10. 升降压电路的设计和分析
  11. C#山寨版本【天翼拨号客户端】---内含详细抓包,模拟数据---万事俱备,只欠东风。
  12. 东西湖职业技术学校计算机,武汉东西湖职业技术学校中专
  13. java计算机毕业设计前后端分离健身房管理系统源代码+数据库+系统+lw文档
  14. c语言编译星座测试,用c语言编写程序,判断输入的日期(月,日)属于哪个星座?...
  15. 高数笔记(二十):无穷级数,级数的审敛法
  16. JSON的格式及Gson 与 FastJson使用
  17. MySql生成日历表
  18. Android之点击Home键后再次打开导致APP重启问题
  19. iphone配置邮箱客户端——以whut邮箱为例(whut.edu.cn)
  20. C#产生不重复的随机数组的方法

热门文章

  1. iPhone 手机、Mac 屏幕画面变黑白的如何解决?
  2. Mal-PEG12-acid,357277-61-3溶解性:水、DMSO、DCM、DMF
  3. 2 - 微信小程序 - 模板与配置
  4. 红包封面人人都可以制作了,0元免审核,还可以直接连接视频号,限时放开
  5. 首次登录界面使用大图片做背景,加载缓慢
  6. Mac内存空间不足如何释放储存空间?深度清理Mac磁盘空间的方法教程
  7. 华为od机试: 叠积木
  8. 【新书推荐】经典畅销官场小说合集(陈玉福等)
  9. three.js学习(二)
  10. (转)什么是AQS??