基础篇

主题:
主题是一个逻辑上的概念,它还可以细分为多个分区,一个分区只属于单个主题,很多时候也会把分区称为主题分区(Topic-Partition)。同一主题下的不同分区包含的消息是不同的,分区在存储层面可以看作一个可追加的日志(Log)文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)

offset 是消息在分区中的唯一标识,Kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,也就是说,Kafka 保证的是分区有序而不是主题有序

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

副本:
Kafka 为分区引入了多副本(Replica)机制,通过增加副本数量可以提升容灾能力。

同一分区的不同副本中保存的是相同的消息(在同一时刻,副本之间并非完全一样),副本之间是“一主多从”的关系,其中 leader 副本负责处理读写请求,follower 副本只负责与 leader 副本的消息同步。副本处于不同的 broker 中,当 leader 副本出现故障时,从 follower 副本中重新选举新的 leader 副本对外提供服务。Kafka 通过多副本机制实现了故障的自动转移,当 Kafka 集群中某个 broker 失效时仍然能保证服务可用。

容灾:
kafka消费者也具备容灾的能力,当消费者使用pull从服务端拉去消息,并且保存了消费的具体offset,当消费者宕机恢复后,会根据之前保存的消费者位置重新进行消费,这样就不会造成消息丢失。

生产者

拦截器:
拦截器是在Kafka0.10.0.0版本中就已经引入的一个功能,Kafka一共有两种拦截器。生产者拦截器消费者拦截器

close() 方法主要用于在关闭拦截器时执行一些资源的清理工作。在这3个方法中抛出的异常都会被捕获并记录到日志中,但并不会再向上传递

Kafka中不仅可以指定一个拦截器还可以指定多个拦截器形成一个拦截器链。拦截器链会根据配置时配置的拦截器顺序来执行(配置的时候,各个拦截器之间使用逗号隔开)。如果拦截器链中的某个拦截器的执行需要依赖上一个拦截器的输出,那么就有可能产生“副作用”。如果第一个拦截器因为异常执行失败,那么第二个也就不能继续执行。在拦截链中,如果某个拦截器执行失败,那么下一个拦截器会接着从上一个执行成功的拦截器继续执行

序列化器:
生产者需要用序列化器(Serializer)将key和value序列化成字节数组才可以将消息传入Kafka。消费者需要用反序列化器(Deserializer)把从Kafka中收到的字节数组转化成相应的对象。

分区器:
消息通过send()方法发送broker的过程中,有可能会经过拦截器,序列化器,之后,就会需要确定消息要发往的分区。如果ProducerRecord中指定了partition字段,那么就不需要分区器的作用。因为partition代表的就是索要发往的分区号

在默认分区器 DefaultPartitioner 的实现中,close()是空方法,而 partition() 方法中定义了主要的分区分配逻辑。如果 key 不为 null,那么默认分区器会对 key 进行哈希(采用 MurmurHash2 算法,具备高运算性能及低碰撞率)根据最终得到的哈希值,与分区的数量取模运算得到分区编号来匹配分区,相同key得到的哈希值是一样的,所以当key一致,分区数量不变的情况下,会将消息写入同一个分区(注意:在不改变主题分区数量的情况下,key 与分区之间的映射可以保持不变。不过,一旦主题增加了分区,那么就难以保证key与分区的映射关系)。如果,key 是 null,那么消息会以轮询的方式写入分区。(注意:如果 key 不为null,那么计算得到的分区号会是所有分区中的一个。如果 key 为 null 并且有可用的分区的时候,那么计算得到的分区号仅为可用分区中的任意一个。)

消息累加器:
RecordAccumulator也叫消息累加器,主要用来缓存消息以便Sender线程可以批量发送,进而减少网络传输的资源消耗来提升性能。 是在客户端开辟出的一块内存区域。

生产者客户端整体架构

kafka设置主题分区数api kafka主题和分区的关系_拦截器


整个生产者客户端主要有两个线程,主线程以及Sender线程。Producer在主线程产生消息,然后通过拦截器序列化器分区器之后缓存到消息累加器RecordAccumulator中。Sender线程从RecordAccumulator中获取消息并发送到kafka中。

RecordAccumulator主要用来缓存消息,这样发送的时候进行批量发送以便减少相应的网络传输。RecordAccumulator缓存的大小可以通过配置参数buffer.memory配置,默认是32M。如果创建消息的速度过快,超过sender发送给kafka服务器的速度,会导致缓存空间不足,这个时候sender线程可能会阻塞或者抛出异常,max.block.ms配置决定阻塞的最大时间。

RecordAccumulator中为每个分区维护了一个双端队列,队列中的内容是ProducerBatch,即Deque,创建消息写入到尾部,发送消息从头部读取。ProducerBatch是消息发送的一个批次,里面包含了一个或多个ProducerRecord

消费者

消费者和消费组:

  • 当消息发布到主题后,只会被投递给订阅它的每个消费组中的一个消费者
  • 每个分区只能被一个消费组中一个消费者所消费。
  • 如果所有的消费者都属于同一个消费组,所有消息都均衡的投递给每个消费者,这是点对点模式
  • 如果所有的消费者都属于不同的消费组,所有的消息都会广播给所有的消费者,这个就是发布/订阅模式
  • Kafka解决重复消费问题使用commitSync()方法拉取最新的位移,commitSync()方法是同步执行的,会耗费一定的性能。

再均衡:

分区的所有权从一个消费者转移到另一个消费者,这样的行为被称为分区再均衡

kafka设置主题分区数api kafka主题和分区的关系_拦截器_02


https://cloud.tencent.com/developer/article/1708081

多线程

  • kafkaProducer线程安全kafkaConsumer线程非安全,KafkaConsumer 中定义了一个 acquire() 方法,用来检测当前是否只有一个线程在操作,若有其他线程正在操作则会抛出 ConcurrentModifcationException 异常.
  • acquire() 方法通过线程操作计数标记的方式来检测线程是否发生了并发操作,以此保证只有一个线程在操作。acquire() 方法和 release() 方法成对出现,表示相应的加锁和解锁操作。

KafkaConsumer实现多线程时通常由两种实现方法:

kafka设置主题分区数api kafka主题和分区的关系_kafka设置主题分区数api_03

主题分区

kafka设置主题分区数api kafka主题和分区的关系_kafka_04


分区副本分配

kafka设置主题分区数api kafka主题和分区的关系_拦截器_05


kafka设置主题分区数api kafka主题和分区的关系_拦截器_06


优选副本选举

kafka设置主题分区数api kafka主题和分区的关系_拦截器_07


kafka设置主题分区数api kafka主题和分区的关系_拦截器_08


kafka设置主题分区数api kafka主题和分区的关系_kafka_09


分区重分配

kafka设置主题分区数api kafka主题和分区的关系_kafka_10

kafka设置主题分区数api kafka主题和分区的关系_kafka设置主题分区数api_11


复制限流

kafka设置主题分区数api kafka主题和分区的关系_kafka设置主题分区数api_12

日志存储

文件目录布局

kafka设置主题分区数api kafka主题和分区的关系_数据_13


消息压缩

kafka设置主题分区数api kafka主题和分区的关系_拦截器_14


kafka设置主题分区数api kafka主题和分区的关系_数据_15


日志索引

kafka设置主题分区数api kafka主题和分区的关系_数据_16


日志清理

kafka设置主题分区数api kafka主题和分区的关系_kafka设置主题分区数api_17


日志压缩

kafka设置主题分区数api kafka主题和分区的关系_数据_18


页缓存

kafka中大量使用了页缓存,这是kafka实现高吞吐量的重要元素之一。

零拷贝

kafka设置主题分区数api kafka主题和分区的关系_kafka_19

服务端和客户端

服务端

时间轮

kafka设置主题分区数api kafka主题和分区的关系_kafka_20


kafka设置主题分区数api kafka主题和分区的关系_拦截器_21


kafka设置主题分区数api kafka主题和分区的关系_数据_22


延时操作

kafka设置主题分区数api kafka主题和分区的关系_拦截器_23


控制器

kafka设置主题分区数api kafka主题和分区的关系_kafka_24

客户端

分区策略

RangeAssignor 分配策略:

kafka设置主题分区数api kafka主题和分区的关系_kafka_25


kafka设置主题分区数api kafka主题和分区的关系_数据_26


RoundRobinAssignor 分配策略:

kafka设置主题分区数api kafka主题和分区的关系_kafka_27


stickyAssignor 分配策略:

kafka设置主题分区数api kafka主题和分区的关系_拦截器_28

事务

kafka设置主题分区数api kafka主题和分区的关系_拦截器_29


kafka设置主题分区数api kafka主题和分区的关系_数据_30


幂等

幂等对接口多次调用所产生的结果和调用一次是一致的。幂等性并不能跨多个分区运作,而事务可以弥补这个缺陷。事务可以保证对多个分区写入操作的原子性。

为什么不支持读写分离

kafka设置主题分区数api kafka主题和分区的关系_数据_31


kafka设置主题分区数api kafka主题和分区的关系_kafka_32


可靠性分析

kafka设置主题分区数api kafka主题和分区的关系_kafka_33

kafka应用

实时流式计算
近几年来实时流式计算发展迅速,主要原因是实时数据的价值和对于数据处理架构体系的影响。实时流式计算包含了 无界数据 近实时 一致性 可重复结果 等等特征。a type of data processing engine that is designed with infinite data sets in mind 一种考虑了无线数据集的数据处理引擎。
1、无限数据:一种不断增长的,基本上无限的数据集。这些通常被称为“流式数据”。无限的流式数据集可以称为无界数据,相对而言有限的批量数据就是有界数据。
2、无界数据处理:一种持续的数据处理模式,应用于上面的无界数据。批量处理数据(离线计算)也可以重复运行来处理数据,但是会有性能的瓶颈。
3、低延迟,近实时的结果:相对于离线计算而言,离线计算并没有考虑延迟的问题。

解决了两个问题,流处理可以提代批处理系统:
1、正确性:有了这个,就和批量计算等价了。
Streaming需要能随着时间的推移依然能计算一定时间窗口的数据。Spark Streaming通过微批的思想解决了这个问题,实时与离线系统进行了一致性的存储,这一点在未来的实时计算系统中都应该满足。
2、推理时间的工具:这可以让我们超越批量计算。
好的时间推理工具对于处理不同事件的无界无序数据至关重要。
而时间又分为事件时间和处理时间。

Kafka Streams
Kafka Streams被认为是开发实时应用程序的最简单方法。它是一个Kafka的客户端API库,编写简单的java和scala代码就可以实现流式处理。
优势:

  • 弹性,高度可扩展,容错
  • 部署到容器,VM,裸机,云
  • 同样适用于小型,中型和大型用例
  • 与Kafka安全性完全集成
  • 编写标准Java和Scala应用程序
  • 在Mac,Linux,Windows上开发
  • Exactly-once 语义

消息路由

kafka设置主题分区数api kafka主题和分区的关系_kafka_34

Kafka监控

监控数据来源

kafka设置主题分区数api kafka主题和分区的关系_拦截器_35


监控模块

kafka设置主题分区数api kafka主题和分区的关系_数据_36


kafka设置主题分区数api kafka主题和分区的关系_拦截器_37