Kafka客户端参数(二)

本文为个人学习笔记整理,仅供交流参考,非专业教学资料,内容请自行甄别。


概述

  在Kafka的客户端中,无论是消费者还是生产者,都有很多的参数,无论是通过Properties去put,还是整合Spring Boot在配置文件中配置,都需要对重要的参数进行理解。


一、生产者消息缓存机制

  Kafka的设计,本来就是应对高吞吐量的场景,所以为了提高效率,消息是不会一条一条地往服务器发的,而是类似于Redis的pipeline和MySQL的批量操作的思想,引入了生产者消息缓存机制
  在生产者消息缓存机制中,有两个重要的组件:RecordAccumulatorSender,前者是作为队列,将消息累计成MemoryRecords发送到服务器。
在这里插入图片描述
  后者是真正将MemoryRecords中的消息发送到服务器的执行者。
在这里插入图片描述
  在RecordAccumulator中会针对每一个Partition维护一个Dequeue,每个队列中存储的是 ProducerBatch(批量消息单元),而非单条消息。
在这里插入图片描述
  Sender是一个独立的后台线程,负责从RecordAccumulator中拉取满足条件的ProducerBatch,批量发送到服务器。
  整体的工作流程:

  1. 生产者调用send方法发送消息。
  2. 消息写入RecordAccumulator中,首先根据其中的Partitioner分区器组件,判断该条消息应该放在哪一个Topic-Partition对应的队列中,并且尝试将消息写入当前队列的最后一个 ProducerBatch。如果当前Batch剩余空间不足,则创建新的ProducerBatch,在写入消息。
  3. 消息并非直接发送,而是当满足以下的条件时,触发发送条件:
    • Batch 大小达到阈值
    • 等待时间达到阈值
    • 生产者调用 flush() 方法(强制刷新缓存)
    • 缓存总内存达到上限,需释放内存。
  4. 后台Sender线程批量发送(底层通过NIO)
  5. 缓存清理和回调
    • 如果服务器ACK返回成功,则释放该ProducerBatch的缓存,触发Callback回调 通知业务线程 “发送成功”;
    • 如果服务器NACK返回失败,则根据重试配置决定是否重试,重试失败则触发 Callback 通知 “发送失败”。

  其中涉及到的参数:BUFFER_MEMORY_CONFIG代表了RecordAccumulator缓冲区的大小,默认是32M。
在这里插入图片描述

  以及BATCH_SIZE_CONFIG代表了RecordAccumulator中每一个batch的大小,默认16k。
在这里插入图片描述

  Sender会在batch中的消息达到最大长度后去发送,但是如果batch中的消息一直都没能放满呢?那么就会在达到超时时间LINGER_MS_CONFIG后进行发送,防止某个batch的消息一直无法发出。
在这里插入图片描述

  基于此可以有一些优化策略,但是总体思想是基于尽可能地将消息批量发送,以减少网络 I/O 次数,但同时要避免为了凑批次而引入不可接受的延迟

  • 吞吐量优先,需要将batch.sizelinger.msbuffer.memory调大。
    • batch.size增大,可以让同一批次发送更多的消息。
    • linger.ms适当增大,可以让同一批次的队列有时间填满消息。
    • buffer.memory增大,是为了防止 batch.size增大后造成的生产者阻塞或者超时。
  • 追求低延迟,需要将 batch.size减少或保持默认,linger.ms减小,buffer.memory默认即可
    • batch.size减小,队列可以尽快被占满,进行消息发送。
    • linger.ms减小,则发送的频次增大。
    • buffer.memory默认,因为每个批次的数据都能被及时发送,基本不会有缓冲区被占满的场景。

二、发送应答机制

  这里的应答机制,不是类似于Rabbit MQ的消费者交换机的确认,而是Kafka服务器客户端生产者批量发送消息的确认。涉及到的参数是ACKS_CONFIG:
在这里插入图片描述
  该参数有三个值:

  • acks = 0 :生产者的Sender组件发送完成消息之后,不关心Kafka服务器是否将消息正常写入,依旧继续执行自己的功能。这样的效率最高,但是可能会丢数据。
  • acks = 1 : 生产者的Sender组件发送完成消息之后,Kafka服务器负责处理消息的leader Partition节点,确认消息收到并且写入成功后,就返回ack给生产者
  • acks = -1 或者 all : 生产者的Sender组件发送完成消息之后,Kafka服务器负责处理消息的leader Partiton节点及所有从节点,确认消息收到并且写入成功后,再返回ack给生产者,这样的效率不高,但是消息完整性是最高的。

  但是当acks = -1 的场景,还可以调整设置,在Kafka服务器的配置中,可以指定min.insync.replicas = n,该参数的含义是,在同一个分区的 ISR 集合中(对于某个特定的 Partition,ISR 集合包含了所有与该 Partition 的 Leader 副本 保持着实时数据同步的副本。这个集合始终包括 Leader 副本本身),有n个节点写入或同步成功,就返回ack给客户端的生产者。

三、生产者消息幂等性

  基于上述的ACK机制,可以引出一个关于生产者消息幂等性的问题。既然是Kafka服务器需要给客户端的生产者ACK,那么ACK消息是不是也有可能会丢失,导致生产者重复发送消息?应对这种情况,Kafka在生产者端有关于重试次数的配置,默认是Integer的最大值,也就是可以理解为无限重试。这样明显是不合理的,服务器会保存大量重复的消息,也就最终造成了消费者重复消费的问题。
在这里插入图片描述
  关于消息幂等性,也有相关的配置:
在这里插入图片描述
在这里插入图片描述
  首先需要补充一个概念,在分布式系统中一致性,有三个语义:

  • 至少一次(At-Least-Once):用户转账,系统错误或者有延迟,没有正确给用户返回结果,用户多次点击转账。
  • 最多一次(At-Most-Once):银行系统接收到用户重复的转账,但是只能接受一笔,剩下的原路退回。
  • 精确一次(Exactly-Once): 用户转账时没有发生系统故障,一次性成功将金额转出到银行系统。

  如果需要保证最多一次,那么直接把acks参数调整为0即可。这种情况是不存在消息重试的问题的,因为客户端生产者默认消息全部发送成功。如果需要保证至少一次,那么需要将acks调整为all,-1或者1,但是这样也没有解决幂等性的问题。如果需要支持精确一次,那么就需要开启幂等性的配置。当开启了配置后,为了保证幂等性,Kafka会:

  • 每个新的生产者在初始化时,都会分配一个唯一的PID,用户是不可见的。
  • 对于该PID,生产者会以Partition的维度,维护一个序列。如果往相同的Partition发送消息时,该序列值会递增,并且和消息一起发到Kafka服务器。
  • Kafka服务器端,也会针对每个PID和Partition的维度,维护一个序列号。只有当客户端的序列值 = 服务端的序列号 + 1 时,服务端才会接收消息,并且将自己的序列号 + 1。否则就认为是重复发送。

四、生产者事务消息

  上述的幂等性,只能解决生产者消息写入单个Partition的情况。如果同一批消息自己指定了key,发送到了多个不同的Partition,并且这些Partition位于不同的服务器上,如何同时保证消息的幂等性?这就是类似于分布式事务的问题了。

补充:为何这种场景无法使用幂等性配置。
假设我消费者A,同一批消息:第一条消息发送到了分区一,第二条消息发送到了分区二。因为幂等性的配置,是单生产者 + 单分区的场景下的,也就是对「同一 PID + 同一 Partition」,只接受序列号递增的消息。Broker 无法判断这两批消息是 “同一批业务相关消息”
那么对于多分区的场景,各个分区只能在自己的范围内保证消息的幂等性,而无法全局保证。就相当于订单服务和库存服务,都使用本地事务,则只能控制自己服务内发生异常情况的回滚,而做不到全局回滚。所以幂等性可以理解为单体项目的事务控制(@Transcational),事务消息可以理解为微服务项目的分布式事务。(seata的@GlobalTranscational)

涉及到几个生产者的api:

// 1 初始化事务
void initTransactions();
// 2 开启事务
void beginTransaction() throws ProducerFencedException;
// 3 提交事务
void commitTransaction() throws ProducerFencedException;
// 4 放弃事务(类似于回滚事务的操作)
void abortTransaction() throws ProducerFencedException;

  但是无论是事务消息,还是幂等性判断,都是只能保证客户端生产者发送消息的可靠性,并不能保证消费者是否从Kafka服务器接收到该条消息,就如同Rabbit MQ 生产者也无法直接知道消费者是否成功消费该条消息一样。

总结

生产者消息缓存机制

在这里插入图片描述

生产者消息幂等性

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值