走近科学之《apache kafka 的秘密》

Approaching science《the secret of apache kafka》

简介、架构设计、broker(工作原理、集群元数据、文件存储机制、文件清楚策略、分区副本、自动平衡、故障处理、ack)、producer(工作原理、分区分配策略、数据可靠性、数据重复性、数据有序乱序性)、consumer(消费方式、group、分区分配及再平衡、事务)、配置参数、调优。


1、kafka 简介

1.1、kafka 定义
  • kafka 传统定义:
      kafka 是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要用于大数据领域的数据实时处理。
  • kafka 最新定义:
      kafka 是一个开源的分布式事件流平台(Event Streaming Platform),被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。
1.2、kafka 场景示例

application-example

1.3、消息队列

  消息队列(或消息中间件)是指利用高效可靠的消息传递机制进行跨进程、与平台无关的数据交流,可以在分布式环境下扩展进程的通信。

1.3.1、消息队列应用场景

  消息中间件的应用场景主要有 解耦、异步、削峰。

  • 解耦:
      解耦是指 A 系统需要跟 B C D 系统进行数据交互(或通信),如果使用传统方式进行通信,则可能会出现由于 B C D 系统挂掉或其它原因导致的通信失败。这时候就可以使用 mq,A 系统将产生的消息放入 mq,自身并不需要关心 B C D 系统是否成功接收到消息,只需要将消息放入 mq 即可。
  • 异步:
      异步是指当用户发送一条请求到 A 系统后,A 系统处理完本地业务可能还需要 B C D 系统处理相关业务,此时请求总耗时将是四个系统处理时间总和。这时候就可以考虑在 A 系统处理完本地业务后将与 B C D 系统处理业务的相关信息放入 mq,然后 A 系统返回。这时的总耗时将是 A 系统处理时间加上放入 mq 的时间。尤其是在互联网业务中用户直接操作的部分,一般要求请求耗时小于 200 ms 左右。
  • 削峰:
      削峰是指一个系统平时的并发量可能只有 100,但某一时刻会暴涨到万、亿级别,假如此时系统使用 mysql 数据库,由于 mysql 的并发量为 2k 左右,那必然扛不住万、亿级别的并发量,最终会导致系统崩溃。这时候就可以将请求先写入 mq,然后系统每秒拉取数据库能承受住的请求数量进行处理,高峰期一过,系统就会慢慢消化掉 mq 中的请求。
1.3.2、消息队列两种模式

  消息队列主要有两种模式,即点对点模式和发布/订阅模式。

  • 点对点模式:
      生产者将生产的消息放入 mq,然后消费者主动拉取数据,收到消息后 mq 中的消息会被删除。
  • 发布/订阅模式:
      用主题 topic 来隔离数据,生产者将消息放入某个主题中,订阅该主题的消费者都可以消费 mq 中的数据,并不会因为某个消费者消费了消息而被删除。
1.3.3、常用 mq 对比

  常用 mq 有 ActiveMQ、RabbitMQ、RocketMQ、Kafka,四者对比如下:

单机吞吐量topic 数量对吞吐里的影响时效性可用性可靠性功能支持
ActiveMQ万级ms 级高,主从架构实现有较低概率会丢失数据mq 领域功能及其完善
RabbitMQ万级us 级高,主从架构实现基本不丢失数据基于 erlang 语言开发,并发能力强,性能好,延时低
RocketMQ十万级当 topic 数量达到几百几千时吞吐量会有小幅度下降,但也是其优势所在,同等机器下可支持大量 topicms 级非常高,分布式架构实现经过配置优化可达到零丢失mq 功能较为完善,分布式架构,扩展性强
Kafka十万级当 topic 数量达到几百几千时吞吐量会有大幅度下降,同等机器下不能支持大量 topicms 级非常高,分布式架构实现经过配置优化可达到零丢失mq 领域功能不完善,只支持简单功能,常用于大数据领域的实时计算和日志采集

2、架构设计

  kafka 基本架构如下图所示:

kafka-framework

2.1、基础架构
  • zookeeper cluster:
      zookeeper 是用来存储维护集群元数据的,在 kafka 2.8 之后开始采用 kraft 模式维护集群元数据,同时也支持用 zookeeper 来维护。
  • kafka cluster:
      kafka 集群由多个 kafka 节点组成。
  • broker:
      broker 即 kafka 节点,即一个 kafka 服务器就是一个 broker,kafka cluster 由多个 broker 组成。eg: broker0、broker1、broker2。
  • producer:
      producer 即消息生产者,一切拥有向 kafka 服务器发送消息的程序都可以称为生产者。eg: producer0、producer1、producer2。
  • topic:
      topic 即主题,producer 是向 topic 里发送消息,consumer 即消费会订阅 topic,从 topic 消费数据。topic 作为中介关联了生产者和消费者。topic 是逻辑存在的。eg: t0、t1。
  • partition:
      partition 即分区,一个 topic 可以分成多个 partition,producer 发送的消息实际上是存放在 partition 里的,每个 partition 里存放了对应 topic 的部分消息。partition 是物理存在的,即其是消息的有效载体,多个 topic 分布在多个 broker 上。eg: t0 有两个分区 p0 和 p1,t1 有一个分区 p0。kafka 分区是一种提高吞吐量、实现负载均衡的实现。
  • replica:
      replica 即副本,一个 topic 的每个 partition 都有若干个副本,这些副本分为 leader 和 follower 两类,leader 只有一个,其余皆是 follower,即每个 partition 会拥有一个 leader 若干个 follower,这些副本分布在多个 broker 上。这是 kafka 可靠性的一种实现。eg: t0 的 p0 有三个副本 r0、r1、r2,其中 r0 是 leader,r1 和 r2 是 follower。分区副本是一种可靠性的实现。
  • leader:
      leader 是每个分区的多个副本中的 “主”,负责对外提供服务,如消息写入和读取。eg: t0-p0-r0。
  • follower:
      follower 是每个分区的多个副本中的 “从”,它不负责对外提供任何服务,会实时从 leader 中同步数据,当 leader 宕机后多个 follower 会参与 leader 选举,其中一个会成为新的 leader。eg: t0-p0-r1。
  • consumer:
      consumer 即消息消费者或订阅者,一切拥有从 kafka 获取消息的程序都可以称为消费者。eg: consumer0、consumer1。
  • consumer group:
      consumer group 即消费者组,由多个 consumer 组成,消费者组内的消费者负责消费不同分区的数据,即一个消费者组内的一个消费者可以消费多个不同分区的消息,但同一个分区只能被一个消费者组内的某个消费者消费。消费者组与消费者组之间互不影响。消费者组在逻辑上算一个订阅者。eg: group0、group1。

3、broker

  集群元数据维护、broker 工作流程、文件存储机制、文件清除策略、分区副本、分区副本分配、leader partition 自动平衡、leader 故障处理、follower 故障处理、ack 应答、高效读写数据。

3.1、集群元数据维护
3.1.1、zookeeper

  zookeeper 中维护的 kafka 集群元数据都在 zookeeper 根路径下(或者在指定路径下,如 zookeeper.connect=ip:port/kafka,则其信息在 kafka 目录下),主要有以下目录:

kafka-zookeeper

  • admin:
      

    /kafka/admin   // 此目录下存放集群管理信息
    /kafka/admin/delete_topics   // 此目录下采用持久存放被删除主题 只有主题名称 没有其它信息 如:
    	[topic-2]   // 表示主题 topic-2 已被删除 
    
  • brokers:
      

    /kafka/brokers   // 此目录下存放 kafka 集群相关信息 如集群节点、主题等
    	/kafka/brokers/ids   // 此目录下采用临时节点存放集群节点信息 如:
    		[0, 1, 2]   // 表示集群中有三个节点 节点号为 0、1、2(全局唯一)
        /kafka/brokers/ids/0   // 此节点为临时节点 存放该 broker 相关信息 如:
        	{ 
        		"listener_security_protocol_map": 
        			{ "PLAINTEXT": "PLAINTEXT" }, 
        		"endpoints": [ "PLAINTEXT://ip:9092"], 
        		"jmx_port": -1,   // jmx 端口号
        		"features": {}, 
        		"host": "ip",   // 所在主机名或 ip 地址
        		"timestamp": "1656618903358",    // 启动时的时间戳
        		"port": 9092,   // 端口号
        		"version": 5 
        	}
    	/kafka/brokers/topics   // 此目录下采用持久节点存放主题信息 如:
    		[topic-0, topic-1]   // 表示集群中有两个主题 分别是 topic-0、topic-1
    	/kafka/brokers/topics/topic-0   // 此目录下存放该主题信息 如:
    		{
    			"removing_replicas": {},
    			"partitions": {   // 当前主题分区信息
    				"2": [0,1,2],   // 分区 id 及 isr 队列 队列值为分区所在 broker id
    				"1": [1,2,0], 
    				"0": [2,0,1]
    			}, 
    			"topic_id": "vdHUu0FuQii3ua4MOCN0ow",   // 主题 id
    			"adding_replicas": {}, 
    			"version": 3
    		}
    	/kafka/brokers/topics/topic-0/partitions   // 此目录下采用持久节点存储该主题的分区信息 如:
    		[0、1、2] 表示该主题有三个分区 分区号分别是 0、1、2
    	/kafka/brokers/topics/topic-0/partitions/0/state   // 此目录下记录了该分区的 leader、isr 队列等信息 如:
    		{ 
    			"controller_epoch": 2,   // controller 选举次数 与 /kafka/controller_epoch 目录下值相同
    			"leader": 2,   // 分区 leader
    			"version": 1, 
    			"leader_epoch": 1,   // 该分区选举 leader 的次数
    			"isr": [2,0,1]   // isr 队列 值为分区所在 broker id
    		}
    
  • cluster:
      

    /kafka/cluster   // 此目录下存放集群信息
    /kafka/cluster/id   // 此目录下存放集群信息 如:
    	{ 
    		"version": "1",   // 版本号 
    		"id": "OFr8HoZhR3Ot8CSXsIxzPQ"   // 集群 id
    	}
    
  • consumers:
      

    /kafka/consumers   // 此目录下采用临时节点存放集群中所有消费者信息(kafka 0.9 版本及之后消费者信息存储在 broker 中 若版本高于 0.9 则此处为空)
    	/kafka/consumers/[group_id]/ids   // 此目录下存放该组内消费者信息 每个消费者都有一个全局唯一 id
    	/kafka/consumers/[group_id]/owners/[topic_name]/[partition_id]   // 此目录下存放该组内所有消费者对应主题及分区信息
    	/kafka/consumers/[group_id]/offsets/[topic_name]/[partition_id]   // 此目录下存放该组内所有消费者对应主题的分区的消费偏移量信息
    	
    // 0.9 版本及之后查看消费者信息可在 broker 节点的 /bin 目录下执行相关命令查看(详细情况见后文)
    
  • controller:
      

    /kafka/controller  // 此目录下存放集群中央控制器 controller 所在 broker 信息 如:
    	{ 
    		"version": 1, 
    		"brokerid": 2,   // 表示目前 broker2 为中央控制器
    		"timestamp": "1656618903330" 
    	}
    
  • controller_epoch:
      

    /kafka/controller_epoch   // 此目录下存放 controller 选举次数 默认从 1 开始 如:
    	2   // 表示目前已选举过两次 controller
    
  • isr_change_notification:
      

    /kafka/isr_change_notification   // 此目录下存放 isr 变更通知信息
    
3.1.2、kraft

  kraft 是 kafka 2.8 以后增加的新特性,旨在代替 zk 来管理集群元数据,即彻底丢弃 zk,kafka 自己来管理。不过此模式还需要时间的检验,且在新版中也同时支持 zk,可按需切换。

  在 zk 模式下,每个 broker 都有一个 controller,且其中一个 controller 会负责跟 zk 交互。而在 kraft 模式中,将每个 broker 上的 controller 提取了出来,放在一起,同时会有一个 controller leader,类似于 controller 的主从架构。

3.2、broker 工作原理

  kafka broker 正常工作原理:
kafka-broker-working-principle

  kafka broker 故障处理:
kafka-broker-fault-process

3.3、文件存储机制

  topic 是逻辑上的概念,而 partition 是物理上的概念,即 partition 是实际存在于 broker 服务器上的。每个 partition 对应一个 log 文件,该 log 文件存储的就是 producer 生产的消息数据。

  producer 生产的数据会不断追加到 log 文件末尾,为防止 log 文件过大导致访问效率低下,kafka 采用了分片和索引机制。分片即将每个 partition 分为多个 segment,每个 segment 可存储 1G 的数据,每个 segment 具体分为 .log、.index、.timeindex 等文件,这些文件位于同一个文件夹下,该文件夹的命名规则是 topic_name + partition_no。

kafka-file-storage-mechanism

  其中 .log 为日志文件,是真正存放数据的地方,.index 是偏移量索引文件,.timeindex 是时间戳索引文件。log 和 index 文件都是以当前 segment 的第一条消息的 offset 命名的。.log 文件中主要记录了当前 segment 开始的偏移量即 starting offset,以及每条消息的 baseoffset、lastoffset、position 和消息数据等。.index 文件中记录了 offset 和 position,每往 .log 文件中写入 4kb 数据,就会往 .index 文件中写入一条索引,即这条索引的 offset 和 position 的值是这批大小和为 4kb 的数据的最后一条消息的 lastoffset 和 position 的值。
message-position

// kafka broker 日志参数配置
1、log.segment.bytes   // segment 大小,默认为 1G
2、log.index.interval.bytes   // 每一个索引项对应的数据体大小,默认为 4kb
3.4、文件清除策略

  kafka 中的文件清除策略由定期删除和过期策略组成(跟 redis 定期删除和过期策略有点相似)。kafka 中的数据默认保存 7 天,此时间可以通过一下参数调整:

// kafka 中日志保留时间
log.retention.hours   // 保留时间,单位 h,默认 7 天,最低优先级
log.retention.minutes   // 保留时间,单位 m
log.retention.ms   // 保留时间,单位 ms,最高优先级
log.retention.check.interval.ms   // 检查周期舍弃,默认 5m

  若 kakfa 中的日志超过设置的时间仍没有被删除则会采用一下两种方式之一。

  • delete:
      delete 删除日志:将过期数据删除。有两种删除方式,分别是 基于时间和基于大小,默认是基于时间。

    log.cleanup.policy = delete   // 启用日志压缩
    1、基于时间:默认打开。以 segment 中所有记录中的最大时间戳作为该文件时间戳。
    2、基于大小:默认关闭。当日志大小超过设置的日志文件总大小时删除最早的 segment(设置日志文件总大小 log.retention.bytes = -1,默认为 -1 表示无穷大)
    
  • compact:
      delete 方式的缺点不能解决一个 segment 中一部分是已过期数据,另一部分是未过期数据的情况,于是就有了 compact,日志压缩。
      日志压缩:对于相同 key 不同的 value 的日志,只保留最后一个版本。

    log.cleanup.policy = compact   // 启用日志压缩
    如压缩之前数据为:
    offset   0   1   2   3   4   5   6   7   8
    key      k1  k2  k1  k1  k3  k4  k5  k5  k2
    value    v1  v2  v3  v4  v5  v6  v7  v8  v9
    压缩之后数据为:
    offset   3   4   5   7   8
    key      k1  k3  k4  k5  k2
    value    v4  v5  v6  v8  v9
    
    注:压缩之后的 offset 可能是不连续的,如上图压缩之后没有 6,当消费消息时,会拿到比 6 这个值大的 offset 对应的消息,即 7。
    
    注:日志压缩只适合特殊场景。如用户 id 为 key,用户信息为 value,若采用日志压缩,则当用户信息修改时,kafka 里保存的将会是最新用户信息。
    
3.5、分区副本基本信息
1、kafka 中分区副本分为 leader 和 follower,leader 负责对外提供读写,follower 只是备份,当 leader 宕机后,某个 follower 会成为新的 leader。
2、kafka 副本的作用时提高数据可靠性。
3、kafka 分区副本个数由 replica factor(副本因子决定),默认副本因子为 1,即默认情况下一个分区会有一个 leader 副本和一个 follower 副本。生产环境一般建议将副本因子设置为 3,即一个分区会有一个 leader 副本和两个 follower 副本。
4、分区副本数量不宜过多,过多会增加磁盘存储空间,且 leader 与 follower 之间要同步数据,会增加网络负担。
5、分区的所有副本统称为 ar(assigned replicas),ar = isr + osr。isr 表示 follower 与 leader 保持同步的副本集合,决定是否同步由 replica.lag.time.max.ms 配置决定,默认 30000ms,即某个 follower 超过 30ms 没与 leader 通话,则认为其故障,然后将其踢出 isr 队列。若 leader 发生故障,则会从 isr 中选举新的 leader。osr 表示 follower 与 leader 同步发生延迟的副本集合。
3.6、broker 分区副本分配

kafka-partition-replica-distribution

  如上图所示,假设有三台服务器,即三个 broker,id 分别为 0、1、2,主题 topic_0 有三个分区分别是 p0、p1、p2,每一个分区有三个副本分别是 r0、r1、r2,假设每个分区的 leader 副本都为 r0(实际情况是分区的三个副本都有可能成为 leader),然后 kafka 会先对 leader 副本分配,即先对 p0-r0、p1-r0、p2-r0 分配,分配时先将 broker 和 partition 按 id 排序,接着会随机挑选一个 broker 来存放 p0-r0,假设随机挑选到了 broker 1,则 p0-r0 对应 broker0,然后按顺序分配,即 p1-r0 对应 broker1,p2-r0 对应 broker2,第二步是分配 follower 分区,之前的分区 broker 与 partition 排序不变,先分配 p0 的 follower 副本,将 p0 的第二个副本 p0-r1 分配到 p0 的 leader 副本 p0-r0 所在的 broker 的下一个 broker 上,然后顺延,即 p0-r1 对应 broker 2、p0-r2 对应 broker 0,p1 和 p2 的 follower 也是同样方式,即 p1-r1 对应 broker 0、p1-r2 对应 broker 1、p2-r1 对应 broker 1、p2-r2 对应 broker 2。

  注:由于 broker 的宕机或增删 broker 节点的情况,会导致多数 leader 副本出现热点分配,或每个 broker 的配置可能不同,所以可以手动对分区进行分配。

3.7、leader partition 自动平衡

  正常情况下,partition 的分区 leader 和 follower 是均匀分布在集群中的所有 broker 上的,来保证每个 broker 的读写吞吐量是均匀的。但是如果某些 broker 发生过宕机,则会导致 partitiion 的 leader 集中分布在某几个 broker 上,这样会导致这些 broker 读写请求压力过大,宕机重启后的 broker 读写请求压力小,从而造成负载不均衡。

  kafka 有自带的 partition leader 自动平衡策略,即 kafka 会自己调整 partition leader 的均匀分配。由以下几个配置决定:

auto.leader.rebalance.enable   // 是否开启自动平衡 默认为 true,开启后,若 leader 的不平衡达到某种比率则会自动进行再平衡
leader.imbalance.per.broker.percentage   // 每个 broker 允许的 leader 不平衡比率 默认为 10%,当 leader 的不平衡比率超过此值,则会进行 leader 再平衡
leader.imbalance.check.interval.seconds   // 检查 leader 是否平衡的时间 默认为 300s,即 kafka 默认会每隔 300s 检查一次 leader 是否平衡
eg: 假设集群中 broker、topic、partition、replica 如下图所示:
topic: tiopic_0   partition: 0   leader: 0   replicas: 0, 1, 2, 3   isr: 0, 1, 2, 3
topic: tiopic_0   partition: 1   leader: 1   replicas: 1, 0, 3, 2   isr: 1, 0, 3, 2 
topic: tiopic_0   partition: 2   leader: 2   replicas: 3, 1, 2, 0   isr: 0, 1, 2, 3
topic: tiopic_0   partition: 3   leader: 3   replicas: 2, 1, 3, 0   isr: 1, 2, 3, 0

如上图所示,集群中有 4 个 broker 分别是 0、1、2、3,一个 topic 即 topic_0,
该 topic 有 4 个分区分别是 0、1、2、3,每个分区有四个副本。

正常情况下每个分区的 leader 应该是存活在 isr 队列中且排在 ar 即 replicas 队列中前面的副本,
即四个分区对应的 leader 所在 broker 节点应该是 0、1、3、2,但实际却是 0、1、2、3,说明分区 2 和 3 所在 broker 发生过宕机重启行为,导致其 leader 与理论选举结果不一致。

leader 不平衡率的计算:
    leader 不平衡率 = 不平衡数 / 分区副本数。
	当分区的 leader 与理论选举结果不一致时不平衡数 + 1,如上图的分区 2 和 3,其不平衡率 = 1 / 4,
	kafka 默认每隔 300s 检查一次不平衡率,1 / 4 > 10%,则 kafka 会对 broker 2 和 3 进行再平衡,
	分区 0 和 1 的 leader 所在的 broker 与理论值一致,即不平衡数为 0,则不需要再平衡。

注:生产环境可不开启 leader 再平衡,因为再平衡期间涉及到的 broker 会暂停对外提供读写服务,从而影响并发能力。
或者可以将不平衡比率调大,以减少再平衡的触发几率。
3.8、leader故障处理
// 当集群中 partition leader 所在的 broker 宕机之后的处理过程
LEO (log end offset): 每个副本的最后一个 offset,实际上可以看成副本最新的 offset + 1
HW (high watermark): 每个分区的所有副本中最小的 LEO

isr: [0, 1, 2]
broker0 leader   : 0   1   2   3   4   5   6   7
broker1 follower1: 0   1   2   3   4
broker2 follower2: 0   1   2   3   4   5   6

如上图所示,当前分区有三个副本,leader 存在于 broker0 节点上,且其 LEO 为 8,
follower1 和 follower2 分别存在于 broker1 和 broker2,其 LEO 分别是 5 和 7。
当前分区的 HW 为 5,即 follower1 副本的 LEO。

注:为什么 leader 的数据是从 0 ~ 7,而 follower 小于这个范围?
	因为 leader 是负责对外处理读写请求的,而 follower 需要从 leader 同步数据,这个同步是需要时间的,所以 follower 的数据会在一定程度上小于 lwader

// 当 leader 宕机后的具体处理流程
1、先将 leader 从 isr 队列中踢出,然后从 isr 中重新选举出一个副本成为 leader
2、为确保 leader 的领导地位,其余 follower 会把各自 log 文件中高于 HW 的部分丢弃,然后从新的 leader 上同步数据
3、当之前宕掉的 leader 重新上线后,会按照 follower 的故障处理方式处理

注:leader 的故障处理方式只能保证副本之间数据一致性,并不能保证数据不丢失或者不重复。
	假设当 HW 为 4 时 leader 宕机了,经过选举 follower1 成为新的 leader,
	然后 isr 中的其它 follower 都向它看齐,也就是在其它 follower 跟新 leader 同步之后实际上所有副本中最大 LEO 才是 5,
	因为在旧 leader 宕机之前新 leader 还没有将 HW 后面的数据(也就是5、6、7)同步过来,此时就造成了数据丢失。
3.9、follower 故障处理
// 当集群中 partition follower 所在的 broker 宕机之后的处理过程
LEO (log end offset): 每个副本的最后一个 offset,实际上可以看成副本最新的 offset + 1
HW (high watermark): 每个分区的所有副本中最小的 LEO

isr: [0, 1, 2]
broker0 leader   : 0   1   2   3   4   5   6   7
broker1 follower1: 0   1   2   3   4
broker2 follower2: 0   1   2   3   4   5   6

如上图所示,当前分区有三个副本,leader 存在于 broker0 节点上,且其 LEO 为 8,
follower1 和 follower2 分别存在于 broker1 和 broker2,其 LEO 分别是 5 和 7。
当前分区的 HW 为 5,即 follower1 副本的 LEO。

// 假设当 follower2 宕机后的处理过程
1、follower2 会先被从 isr 队列中踢出
2、在 follower2 重新上线的这期间 leader 和 follower1 仍会继续工作,假设当 leader 和 follower1 的 LEO 变为下图所示了 follower2 重新上线
isr: [0, 1]
broker0 leader   : 0   1   2   3   4   5   6   7   8   9
broker1 follower1: 0   1   2   3   4   5   6   7
3、follower2 上线后会先从本地磁盘读取宕机前的 HW,然后将 log 文件中高于 HW 的部分丢弃
4、然后开始从 leader 同步数据
5、当 follower2 的 LWO 大于等于该分区的 HW 时(宕机前记录的 HW 是 4,此时分区 HW 是 8,从 leader 同步数据同步到位置 8 时),
该 follower2 就可以加入到 isr 队列了
3.10、ack 应答

   ack 应答是指生产者向 kafka 发送消息时 kafka 服务器对生产者端的一种回应,是 kakfa 的一种可靠性机制。

// ack 应答共有三个级别
1、0: 生产者发送过来的数据,不需要数据应答就会应答。
2、1: 生产者发送过来的数据,leader 收到落盘之后会应答。
3、-1(all): 生产者发送过来的数据,isr 队列中的所有副本都收到之后才会应答。即 leader 和 存活的 follower 都收到之后应答。
3.11、高效读写数据
1、kafka 本身是分布式集群,且采用了分区技术,并行度高:
	分布式集群本身就可以提高系统的并发能力,而分区技术一方面提高了生产者端和消费者端的生产和消费能力,
	另一方面又解决了海量数据的存储问题。

2、访问数据采用稀疏索引,可以快速定位要消费的数据:
	kafka 的数据存储机制中,是按照 segment 为单位进行存储的,且大小为 1G,为其设置了稀疏索引,
	在增加索引能力的同时又减少了索引数据大小。

3、顺序写磁盘:
	kafka broker 在接收到生产者生产的数据后,要进行落盘,在写入 log 文件时是采用一直追加在文件末端的方式
	进行的,减少了大量磁盘寻址的时间,叫做顺序写。有数据表明,顺序写可以达到 600m/s,而随机写只有 100k/s。

4、页缓存 + 零拷贝技术:
	页缓存:
		页缓存(Page Cache)是操作系统内部的缓。当生产者发送数据时(写入),实际上是将数据发送到了页缓存;
		当消费者消费数据时(读取),会先从页缓存中查找,若没有,则再将对应文件加载到页缓存中在查找。
		实际上页缓存是把尽可能多的空闲内存当作磁盘来使用,唯一区别就是页缓存(内存)的读写要比磁盘快很多。
	零拷贝:
		零拷贝是指当消费者消费数据时,数据会被直接从页缓存发送到消费者端,而不会先加载到 kafka 服务端的
		业务层,再发送给消费者。

4、producer

  发送基本原理、分区分配、吞吐量提高、可靠性、重复性、有序性、乱序性。

4.1、producer 发送基本原理

producer-send-principle

4.1.1、producer main thread

  生产者的 main thred 主要对消息进行后置处理。

1、Interceptors 拦截器:
	拦截器可以对消息进行特殊化处理,自定义实现。
	
2、Serializer 序列化器:
	序列化器主要对消息进行序列化,以便传输。
	需要注意的是,kafka producer 客户端是由 java 编写的,但此处序列化并没有使用 jdk 自带的序列化器,而是 kafka 自己设计的序列化器。
	原因是 jdk 的序列化方式太过繁重,会使消息体过大,kafka 自己的序列化方式使得序列化结果轻便,更适合在大数据场景下使用。
	
3、Partitioner 分区器:
	分区器的主要作用是决定该条消息发往该主题的那个分区,kafka 默认提供了几种分区策略,后续有讲解。当然也可以自定义实现分区策略。
4.1.2、producer record accumulator

  生产者的缓存队列是用来临时存放包装后的消息体的。

1、以分区为单位进行队列,即发往同一分区的消息会进入同一的队列。
2、队列中的数据是以批次为单位,即可能多条消息为一批次,这是由批次大小决定的。batch.size,批次大小,默认为 16k。
3、这个队列其实是个双端队列,默认大小为 32m
4.1.3、producer sender thread

  生产者 sender thread 的主要作用是将缓存队列中的数据发送到 kafka 服务器。

1、发送条件,即 sender 什么时候发送?由 batch.size 或 linger.ms 决定。
	batch.size: 缓存队列中每批次大小,默认为 16k。即当队列中某批次的数据达到 16k 时,sender 线程就会将其取走,准备发往 kafka。
	linger.ms: sender 等待批次达到 batch.size 的时间,默认为 0ms。
		即当 sender 线程等待时间超过 linger.ms 批次大小还未达到 batch.size 的值,那么 sender 就会直接取走这批数据,并不会继续等待。
		默认是 0ms,说明默认情况下队列中进来一条数据,sender 就会拿走一条。
     注:以上两种条件满足其一 sender 就会取走数据,即或大小达到设置的批次大小,或等待时间超过设置的等待时间。
     
2、发送方式:
	Selector,链路器会链接器,负责与 kafka 服务器建立连接、发送数据、并接受 kafka 的反馈结果。
	因为缓存队列是以 partition 为单位的,而 partition 是放在 broker 上的,所以 sender 会以 broker id 为 key,request 为 value 进行缓存,
		针对每个 broker 最多可以缓存五个发送请求,即当 selector 发送完第一个请求之后暂时还没收到 kafka 的 ack,那么它会继续发送下一个,
		直到累计的请求缓存数量达到五个时还没有接收到第一个请求的 ack,那么 selector 就不会再往该 broker 发送数据。
	注:sender 发送方式分为同步、异步和带回调发送。
	
3、结果处理:
	成功:若 selector 接收到了此次发送请求 kafka 的 ack 应答,则会清除掉缓存起来的这次请求,并清理掉对应缓存队列中的数据。
	失败:若 selector 久久未收到 ack,那么它会重试,默认重试次数为 Integer.MAX_VALUE,即它会跟 kafka死磕。
4.2、producer 分区分配策略

  kafka producer 客户端设计了一个 Partitioner 接口,负责分区处理,并为我们提供了一个默认的实现 DefaultPartitioner,该实现提供了三种分区策略。

1、若发送消息时指定了分区,则以指定的分区为目标分区。
2、若发送消息时未指定分区但指定了 key,则以 key 的哈希值对当前主题分区数的模值为目标分区。
3、若发送消息时未指定分区,也未指定 key,则会采用 sticky partition,即黏性策略。
	黏性策略是指,分区器会先随机选一个分区,并尽可能一直使用该分区,待批次已满(batch.size = 16k)或等待时间已到(linger.ms = n ms),
	则会再重新随机选一个和上次不一样的分区作为目标分区。

  kafka 自带的分区策略可能不能满足某些特殊场景的业务需求,因此我们可以通过自定义分区器来实现。即实现 Partitioner 接口即可。

// 自定义 partitioner
public class MyPartitioner implements Partitioner {

    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        // 可以根据 topic、key、value 来指定分区
        // eg:
        if (/** topic、key、value 符合某个条件 */) {
        	// 则返回相应的分区 
            return 0;
        } else {
            return 1;
        }
    }
}

// 将自定义的 partitioner 指定给 "partitioner.class" 配置
4.3、producer 提高吞吐量
1、batch.size: 批次大小,默认 16k,可调整至 32k 等,通过增大批次大小来提高每次发送的数据量。(该仅在 linger.ms 不为 0 的情况下有效)
2、linger.ms: sender 等待时间,默认 0ms,建议设置为 5 ~ 100ms,提高资源的利用率。
3、compression.type: 压缩 snappy。
4、RecordAccumulator: 增加缓冲区大小。
4.4、数据可靠性

  producer 到 broker 的数据可靠性可以通过设置 ack 来保证,即 kafka ack 应答。

1、ack = 0: broker 收到 producer 的数据就会应答,可靠性最低,一般不使用。
2、ack = 1: broker 收到 producer 的数据且 leader 落盘后才应答,可靠性中等,一般用于发送日志等。
3、ack = -1(all): broker 收到 producer 的数据 leader 落盘且 isr 队列中的 follower 收到且落盘后才应答,可靠性最高,一般用于发送金钱相关的数据。

? 若该分区只有一个副本,不管 ack 是什么,只要该副本所在 broker 宕机就会丢失数据。所以分区副本至少得有俩;
? 若 isr 队列中只有 leader 一个,也会出现 leader 宕机丢失数据的情况。所以 isr 队列中副本数也至少得有俩;

故,只有当 ack = -1(all) && replica >= 2 && isr.size >= 2 才能完全保证数据的可靠性。

缺点:ack = -1(all) 时可能会造成数据重复(很小概率)
	假设 leader 收到一批数据,leader 已落盘,follower 已主动同步且已落盘,在 leader 对 producer 应答的一瞬间 leader 宕机了,
	此时,其中某个存活的 folloer 会成为新的 leader,当 producer 没有收到 leader 的应答后,会重新发送该批数据,且会发送给新的 leader,
	当该 leader 在作为 follower 时已经同步了一份儿了,此时再接收一份儿,就会造成数据重复。
4.5、数据重复性
4.5.1、数据传递语义
1、at least once: 最少一次,即 producer 发送的消息,broker 最少接收一次,可以通过 ack = -1(all) && replica >= 2 && isr.size >= 2 实现,
	即一定能保证数据不丢失,但不保证数据不重复。
2、at most once: 最多一次,即 producer 发送的消息,broker 最多接收一次,可以通过 ack = 0 实现,即一定能保证数据不重复,但不保证数据不丢失。
3、exactly once: 精确一次,即 producer 发送的消息,broker 需要做到不丢失也不重复。
	exactly once 可以通过 at least once + 幂等性/事务实现,幂等性和事务是 kafka 0.11 版本之后加入的新特性。
4.5.2、幂等性
    幂等性是指对于同一条消息,无论 producer 向 broker 发送多少次,broker 都只会持久化一条,因此,幂等性可以保证不重复。
即 exactly once 可以通过 at least once + 幂等性实现。

broker 判断是否重复是通过 <PID, Partition, SeqNumber> 联合主键实现的,即对于联合主键相等的消息,broker 只会持久化一条。
	PID: 生产者唯一标识,由 broker 分配,且 broker 每次重启都会为生产者分配一个新的唯一标识,所以对于同一个 PID 我们称之为单会话。
	Partition: 分区编号。
	SeqNumber: 即 Sequence Number,消息序列化,单调递增。
	
    对于同一会话(即单会话),其 PID 可能相同;对于同一分区,其 SeqNumber 可能相同;故幂等性只能保证在单会话、单分区内不重复。
    
    可以通过 enable.idempotence = true 来开启幂等性,默认为 true。
4.5.3、事务原理
首先,使用 kafka 事务,必须开启幂等性,
其次,producer 在使用事务时必须自定义一个唯一的 transactional.id,这样即便客户端宕机,重启之后也能完成未完成的事务。

// kafka 事务原理
    kafka 内部使用一个内部主题来存储事务信息,即 _transaction_state-[分区]-leader,该主题有 50 个分区,每一个分区负责一部分事务。
    kafka 每个 broker 节点都有一个 TransactionCoordinator 事务协调器,负责处理事务。
    对于每一个事务,会根据其 transactional.id 对 50 取模来得到存储该事务的分区,该分区所在的 broker 即是处理该事务的 TransactionCoordinator 所在的节点。
    
// 工作流程
1、producer 向 kafka 请求 PID(幂等性需要)
2、broker 返回 PID
3、producer 发送消息
4、producer 向 对应事务协调器发送 commit 请求(即提交事务)
5、事务协调器将 commit 请求持久化到对应主题分区(即将此次事务信息持久化)
6、事务协调器向 producer 返回 commit 提交成功
7、事务协调器在内部向此次消息发送的 partition leader 发送请求,询问该消息是否持久化成功
8、partition leader 返回成功后,事务协调器将成功信息持久化到对应主题分区
9、事务结束
4.6、数据有序性

  kafka 只能保证在单分区内数据有序,即每个分区内的数据是有序的,想要保证数据有序就必须是单分区。

  想要保证多分区情况下的数据有序,可以考虑在消费者端处理,如在消费者端对消息进行重排序,这样可以保证数据有序,但会在某种程度上影响消费者端的吞吐能力。

4.7、数据乱序性

  kafka 数据乱序的原因如下:

在生产者端最多可以缓存五个等待 broker 的 ack 应答的请求,也就是说,当 sender 线程发送完第一个请求之后,
在未收到 broker ack 应答的情况下还可以继续发送第二个、第三个...,并会将这种未收到 ack 的请求缓存起来,
但这种未收到 ack 的请求最多只能缓存五个,当超过五个请求没收到 broker ack 后,生产者将不会再给该 broker 
发送请求。
在这种情况下,假如 broker 先后收到请求一、请求二,落盘后并返回 ack,生产者发送请求三时失败了,则它会一边
接着发送请求四、请求五...,一边重新发送请求三,假设 broker 在收到请求四落盘并返回 ack 后,请求三也发送
成功了,此时,消息的顺序将变成 3、4、2、1(正常应该是 4、3、2、1),这就造成了消息的乱序。

  乱序的解决办法如下:

数据乱序的解决办法:
kafka 1.x 版本前:
	max.in.flight.requests.per.connection = 1,即设置生产者端最多只能缓存一个请求,也就是说,
	当某个请求发送失败了,sender 线程会一直尝试重新发送,在此期间不会发送下一条消息。
kafka 1.x 版本及以后:
	未开启幂等性:
		max.in.flight.requests.per.connection = 1(和 1.x 版本前一样)
	开启幂等性:
		max.in.flight.requests.per.connection 的值 <= 5 即可。
		原因是,kafka 服务端会缓存生产者端发送的最近 <= 5 个请求,对其按照幂等性中的 SeqNumber 重新排序后
		再落盘,这样就保证了数据的有序性。

5、consumer

  消费方式、总体工作流程、详细工作流程、消费者组初始化原理、分区分配与再平衡、事务、提交 offset、吞吐量提高。

5.1、消费方式

  有两种消息消费方式,分别是 pull 和 push。kafka 采用的是 pull 的方式。

1、pull: 
	即 拉,消费者主动从消息队列拉取消息。
	优点是由消费自己决定消费速率,即我每秒能消费多少数据,那我就每秒拉取多少数据。
	缺点是如果消息队列中没有消息,那消费者可能陷入循环中,一直返回空。
	
2、push:
	即 推,由消息队列主动推送到消费者。
	优点请度娘。
	缺点是消息队列的推送速率可能不匹配消费者的消费速率。
5.2、consumer 总体工作流程

kafka-consumer-working-process

1、消费者主动从 broker 拉取数据。
2、消费者的 offset 表示该消费者消费消息的位置,即消费到那儿了,该 offset 会被持久化到系统主题中(_consumer_offsets)。
	(0.9 版本之前是存放在 zk 中的,但会造成 broker 与 zk 频繁的数据交互,降低吞吐率)
3、_consmuer_offsets 即系统主题,默认 50 个分区,跟存储事务信息的主题  _transaction_state 分区数一致。
4、一个消费者可以同时消费多个分区的数据。
5、消费者组内,每一个消费者可以同时消费多个分区的数据,但不能多个消费者同时消费一个分区的数据(容易造成重复消费、漏消费等问题)。
5.3、consumer group 工作原理

kafka-consumer-group-init-process

    首先,在 kafka 每个 broker 上面都会有一个 GroupCoordinator,即消费者组协调器(与事务有关的是 TransactionalCoordinator),这个组协调器用来辅助
实现消费者组的初始化及分区分配。
	kafka 会根据 group_id 来确定该消费者组由那个 broker 上的协调器协调。具体确定方式是先根据 groupi_id 对 _consumer_offsets 主题的分区数取模
(默认是 50),得到的模值是 _consumer_offsets 主题的分区编号,该分区在那个 broker 上,就选择该 broker 上的 GroupCoordinator 来协调改组。同时,
改组内所有消费者提交的 offset 最终都会持久化到该分区。

	确定完组协调器在那个 broker 上后:
	1、组内的每个消费者都会向该组协调器发送 join group 的请求。
	2、组协调器会随机选择一个 consumer,来作为 consumer leader,然后将注册进来的 consumer 和涉及到的主题及分区信息发送给该 consumer leader,
该 consumer leader 的主要作用是指定消费方案(即分区分配,后续会有说明)。
	3、consumer leader 制定完消费方案后,会将方案发送给组协调器。
	4、组协调器收到消费方案后,将方案下发到组内所有消费者。
	
	注:组内每个消费者都会和组协调器保持心跳(默认 3s 发送一次),一旦超时(session.timeout.ms = 45s),该消费者会被移除,并触发再平衡;
		若消费者处理消息时间过长(max.poll.interval.ms = 5min),也会触发再平衡。
	再平衡后续会有说明,再平衡的意义是在该消费者出现问题后,其所负责消费的分区的数据能够继续被消费。
5.4、consumer 详细工作流程

kafka-consumer-detail-working-process

1、consumer 发送消费请求
2、创建消费者网络客户端,网络客户端负责与 broker 交互
3、发送拉取请求,数据返回由 fetch.min.bytes 和 fetch.max.wait.ms 决定,及若数据大小满足 fetch.min.bytes 
	或 consumer 客户端等待时间超过 fetch.max.wait.ms 就会以回调的方式返回数据,同时由 fetch.max.bytes 规定了每次拉取的最大大小
4、通过 onSuccess 回调获取数据,并将数据放入内存队列
5、从队列中获取消息,每次最大获取 500 条,由参数 max.poll.records 决定,默认 500
6、反序列化、拦截器、处理
5.5、consumer 分区分配及再平衡

  consumer 分区分配是说当集群中有多个分区,同时消费者组内有多个消费者时,那个分区由那个消费者消费。也就是上文提到的消费方案的问题。这个是由 consumer leader 决定的。kafka 为我们设计了四种分区分配策略,即 Range、RoundRobin、Sticky、CooperativeSticky,默认是 Range + CooperativeSticky。同时,用户还可以自定义分区策略。

  • Range 及再平衡:

    	Range 是针对每个 topic 而言的。
    	先将该 topic 的所有分区按分区号排序,同时将订阅这个 topic 的所有消费者按字母顺序排序;然后用 partition 数 / consumer 数 = m 余 n,此时,若
    余数 n 为 0,则每个消费者消费 m 个分区,若 n 不为 0,则排在前 n 个位置的消费者每人将多消费一个分区。
    	
    	eg:
    		假设 topic0 有 7 个分区,分区号分别是 0、1、2、3、4、5、6,订阅该主题的消费者有三个,分别是 c0、c1、c2,那么分区分配结果将是:
    			c0: 0、1、2
    			c1: 3、4
    			c2: 5、6
    		
    	再平衡:
    		当消费者 c0 宕机后,在 45s 以后会触发再平衡(因为 consumer 会每隔 3s 跟 GroupCoordinator 心跳一次,若超过 45s 未心跳,则该消费者会被移除),再平衡是指此前由 c0 负责消费的分区将会被其它消费者消费。
    		在 Range 分区分配策略下,当某个消费者宕机,其所负责的分区任务将全部由另外某个消费者接手。
    		eg: 
    			在上面的示例中,当 c0 宕机后,其所负责的 0、1、2 分区的数据将全部由 c1 或者 c2 负责。
    			
    	注:
    		因为 Range 是针对每个 topic 的,所以在少量 topic 下该策略是没有问题的,但若在大量 topic 下,排在前面的消费者将比排在后面的消费者多消费 
    	n 个 分区,会出现明显的数据倾斜问题。
    		同时,其再平衡方式也会使某个消费者压力过大。
    
  • RoundRobin 及再平衡:

    	RoundRobin 是针对集群中所有 topic 而言的。
    	它会将集群中所有 topic 的所有分区和所有消费者都列出来,然后对其按照 hash code 排序,最后采用轮询算法来分配分区。
    	
    	eg:
        	假设 topic0、topic1 共有 7 个分区,分区按 hash code 排序结果是 0、1、2、3、4、5、6,订阅这两个主题的消费者有三个,
        分别是 c0、c1、c2,那么分区分配结果将是:
        		c0: 0、3、6
        		c1: 1、4
        		c3: 2、5
        		
        再平衡:
        	会将宕机 consumer 所负责的分区再以 RoundRobin 的策略分配给其它消费者。
        	eg:
        		c1: 1、4、0、6
        		c2: 2、5、3
    
  • Sticky 及再平衡:

    	Sticky 策略跟 Range 策略类似,唯一区别是 Range 会先排序,而 Sticky 不会排序,随机分配。
    	
    	eg:
        	假设 topic0 有 7 个分区,分区号分别是 0、1、2、3、4、5、6,订阅该主题的消费者有三个,分别是 c0、c1、c2,那么分区分配结果将是:
    			c0: 1、4
    			c1: 0、3、5
    			c2: 2、6
    			or
    			c0: 2、4、5
    			c1: 1、3
    			c2: 0、6
    			or
    			...
    			
    	再平衡:
    		其在再平衡方式依旧是 Sticky。
    
5.6、提交 offset
自动提交:
	自动提交是指消费者客户端自动帮助我们提交 offset。
	enbale.auto.commit: 是否开启自动提交 offset 功能,默认为 true
	auto.commit.interval.ms: 自动提交 offset 的时间间隔,默认为 5s
	若开启了自动提交 offset,则 consumer 客户端会默认每个 5s 提交一次 offset,其与消费者消费数据是并行执行的,即其提交的 offset 可能小于消费者正在
消费的 offset,也有可能大于消费者正在消费的 offset。

	注:自动提交可能会造成消息的重复消费。
		如自动每 5s 提交一次 offset,假设上次提交的 offset 是 4,此时消费者正在消费的 offset 为 7,中间的
	5、6 都已经被消费了,还没到 5s 时消费者挂了,消费者重启后从 broker 加载到的 offset 为 4,会导致后面的 
	5、6 被再消费一次。

手动提交:
	手动提交是指开发者在代码中自己通过相关代码提交 offset。
	enbale.auto.commit = false,关闭自动提交,即可使用手动提交 offset。
	手动提交分为 commitSync(同步提交)和 commitAsync(异步提交),两者的相同点是都会将本批次需要提交的数据的最高 offset 提交;不同点是同步提交会阻塞当前线程,且会失败重试,异步提交不会阻塞当前线程,也没有失败重试。
	
	注:手动提交可能会造成漏消费。
		如消费者在提交 offset 时,可能处理的数据还在内存中,未落盘,此时消费者挂了,但提交 offset 的这个请求
	已经发出去了,消费者重启后将从下一个 offset 开始消费,这就导致了漏消费。
5.7、consumer 事务
	consumer 事务的出现是为了确保消息一次性消费,即不重复也不缺失,即精确消费。
	实际上消费者端想要实现事务,其所连接的下游必须支持事务才可以,这样才能做到消费消息与提交 offset 的原子性。
如消费的消息要写入数据库,那得这个数据库支持事务才可以,若要写入其它系统,那得这个系统支持事务才可以。

	消息从 producer 到 kafka 到 consumer 到下游的精确性:
		ack = -1(all) && 
		开启幂等性 && 
		replicas >= 2 && 
		isr.size >= 2 && 
		手动提交 offset && 
		下游支持事务
		以上这些条件都满足时,可以保证消息从生产者到 kafka,再到消费者,再到下游,不重复、不丢失。
5.8、consumer 提高吞吐量
1、增加分区数,同时增加消费者数,最好做到分区数 = 消费者数。
2、提高每批次拉取的数量(需注意增大批次量时可能需要同时增大每批次允许的大小)。
	每批次拉去数量过少(拉去数量 / 处理时间 < 生产速度),即消费者单位时间内处理的数据量小于生产者单位时间内生产的数据量,会造成数据积压。

producer -> kafka -> cosnumer -> 下游整体吞吐量的提升:
	1、batch.size: 批次大小,默认 16k,可调整至 32k 等,通过增大批次大小来提高每次发送的数据量。(该仅在 linger.ms 不为 0 的情况下有效)
	2、linger.ms: sender 等待时间,默认 0ms,建议设置为 5 ~ 100ms,提高资源的利用率。
	3、compression.type: 压缩 snappy。
	4、RecordAccumulator: 增加缓冲区大小。
	5、增加分区数,同时增加消费者数,最好做到分区数 = 消费者数。
	6、提高每批次拉取的数量(需注意增大批次量时可能需要同时增大每批次允许的大小)。
	7、消费者端可以多线程消费。

6、kafka broker、producer、consumer 重要配置参数

kafka-configuration

7、kafka 调优

  kafka 调优主要从提高吞吐量、分区设置、数据精准性、数据有序性、单条日志大于 1m、服务器宕机等这些角度着手。

7.1、提高吞吐量
1、提高生产者吞吐量
	a、buffer.memery: 发送消息的缓冲区,默认 32m。可以增加到 64m。
	b、batch.size: 缓存到队列的批次大小,默认 16k。如果太小会导致频繁网络请求,吞吐量下降,若太大则会导致消息延迟。
	c、linger.ms: 发送消息的延迟,默认 0ms。一般设置 5 ~ 100ms,太小会导致频繁网络请求,吞吐量下降,太大导致消息延迟。
	d、compression.type: 消息压缩方式,默认 node 即不压缩。适当压缩可提高吞吐量,但会加大 producer 端的 CPU 开销。
	
2、增加分区
	kafka 自带特性。
	
3、提高消费者端吞吐量
	a、fetch.max.bytes: 拉取数据的最大大小,默认 50m。可适当增加。
	b、max.poll.records: 拉取数据的最大条数,默认 500 条。可适当增加。
	
4、增加下游消息处理能力
	如多线程处理等等。
7.2、分区设置
1、首先针对一个 topic 只创建一个分区。
2、压测这个 topic 的 producer 和 consumer 的吞吐量。
3、假设压测结果是 a、b,单位为 mb/s。
4、假设总目标吞吐量为 c,那么分区数 = c/min(a, b)。
	eg: a、b、c 分别是 20m/s、30m/s、100m/s,那么分区数则可以为 100/20 = 5。
	实际生产环境分区数一般为 3 ~ 10 个。分区数并不是越多越好,需要根据实际业务需要进行压测。
7.3、数据精确性
1、producer 端
	a、acks = -1。
	b、enable.idempotence 为 true,开启幂等性。
	
2、broker 端
	a、分区副本因子大于等于 2。
	b、isr.size 大于等于 2。
	
3、consumer 端
	a、事务 + 手动提交 offset。
	b、consumer 下游必须支持事务,如 mysql 等。
7.4、数据有序性
1、producer 端
	a、单分区内有序。
	b、若开启了幂等性则 max.in.flight.requests.per.connection 应设置为 1 ~ 5。
	c、可以以 key 为维度进行分组,同一个 key 有序,消费者端可以根据 key 做相应处理。

2、consumer 端
	a、单分区内有序,即若只消费一个分区的数据,则其是有序的。
	b、根据 key 分组。若生产者发送的数据指定了 key,则消费者端可以以 key 为维度入内存队列,然后再处理。
7.5、单条日志大于 1m
1、message.max.bytes: broker 端接收每批数据的最大值,针对 broker,默认 1m。
2、max.request.size: broker 端接收每批数据的最大值,针对 topic,默认 1m。
3、replica.fetch.max.bytes: 副本同步数据时每批数据的最大值,默认 1m。
4、fetch.max.bytes: 消费者从 broker 拉取一批数据的最大字节数。默认 52428800(50m)。
	实际拉取时每批数据大小最大值受 message.max.bytes(broker.configuration) or max.message.bytes(topic.configuration)。
7.6、服务器宕机
1、重启大法。
2、若重启不行则考虑增加内存、增加 CPU、网络宽带。
3、若 kafka broker 被误删,此时若副本因子大于等于 2,则可按照服役新节点的方式重新服役一个新节点,并执行负载均衡。

  遥望你倩影经过,看灯饰与夜色辉映。 蔡枫华《倩影》.mp3

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值