2.Kafka生产者
2.1生产者流程
Kafka生产端(Produer)由两个线程完成消息的推送:
一个是主线程,用户端调用*kafkaProducer.send(ProducerRecord)*推送消息是通过主线程完成。推送的数据首先被缓存到双端队列消息累加器RecordAccumulator。
另外一个是sender IO线程,其不断轮询RecordAccumulator,满足一定条件后,就进行真正的网络IO发送,使用的是异步非阻塞的NIO。主线程的send方法提供了一个用于回调的参数,当sender线程发送完后,回调函数将被调用,可以用来处理成功,失败或异常的逻辑。
具体处理逻辑如下图所示:
Step1**:主线程工作
在主线程,KafkaProducer调用send方法推送ProducerRecord创建的消息。该消息经过拦截器、序列化器、分区器最终加载到消息累加器。
拦截器:在消息发送前,可以按照某周规则过滤消息或者修改消息等。
序列化器:生产者需要用序列化器将key和value序列化成字节数组才可以将消息传入Kafka。
分区器:确定消息发送的分区,Kafka实现了不同的分区策略
消息累加器:主要用来缓存消息以便Sender线程可以批量读取、发送,进而减少网络传输的资源消耗来提升性能。 消息累加器是在客户端开辟出的一块内存区域,可以通过参数buffer.memory配置,默认值为 33554432B ,即 32M。RecordAccumulator的内部为每个分区都维护了一个双端队列。队列中的具体内容就是ProducerBatch(消息批次)。其中消息批次ProducerBatch,可以由多个较小的ProducerRecord(消息)组成。ProducerBatch大小可以通过batch.size控制,默认16kb。
Step2:消息加入消息累加器
分区器会为每条消息分配分区,确定分区的消息写入消息累加器时,会找到该分区对应的双端队列。在确定分区对应的双端队列以后,会向双端队列尾部的ProducerBatch加入消息。该操作执行完以后,主线程就只需要等待Sender线程轮询读取消息并执行返回结果。
Step3:Sender线程读取消息及消息发送
Sender线程不断轮询消息累加器,寻找达到发送要求的分区 。如:在分区中ProducerBatch内数据积累到batch.size(16kb),或者如果分区中ProducerBatch内数据迟迟未达到batch.size,Sender线程等待linger.ms设置的时间到了之后也会获取数据。linger.ms单位ms,默认值是0ms,表示没有延迟。
将轮询获得的各个ProducerBatch按照目标分区所在的leader broker进行分组
将分组后的batch通过底层创建的Socket连接发送给各个Kafka实例
请求在从 sender程发往 Kafka前还会保存到 InFlightRequests中,它的主要作用是缓存了已经发出去但还没有收到服务端响应的请求。InFlightRequests默认每个分区下最多缓存5个请求,可以通过配置参数为max.in.flight.request.per. connection修改。
等待服务器端发送response回来
Step4:Sender线程处理响应
发送后,需要等待kafka的应答机制,取决于配置项ack。Request请求接受到kafka的响应结果,如果成功的话,从InFlightRequests清除请求,否则的话需要进行重发操作,可以通过配置项retries决定,当消息发送出现错误的时候,系统会重发消息。retries表示重试次数。默认是 int 最大值,2147483647。
Step5:清除消息累加器消息
2.2Kafka生产者如何保证发送的消息不丢
消息丢失原因:网络延迟、或者Kafka实例宕机都会导致消息的丢失
Kafka通过ack确认机制来感知消息有没有丢失,通过合理设置ack可以确保消息不丢
ack确认机制分为三个级别:
ack=0:此时消息发送出去,生产者就不管消息了。即使消息因网络问题未发送到Kafka实例。此时效率最高,但安全性最低。
ack=1:生产者发送消息后,当Leader接受到消息会向生产者发送确认信息,确认消息已经发送成功。生产者收到效应后会清除消息累加器中的消息。当follower副本还没同步Leader副本中消息时,Leader宕机,此时数据也会丢失。
ack=-1或者ALL:生产发送消息后,当Leader收到消息和ISR中所有副本都同步到消息后才像生产者发送确认消息,确认消息已经发送成功。生产者收到效应后会清除消息累加器中的消息。此时数据在多个副本都存在,几乎不会丢失。
2.3Kafka生产者如何保证发送的消息只发一次
消息多发原因:在消息发送后,需要接受Kafka实例发来的确认信号。如果确认信号丢失、或者未在规定时间返回,此时生产者会认为消息没有发送成功。此时会重发消息,就会造成消息重复。对应三种情况。
最多一次(At Most Once),此时ack=0可以保证消息不重复(仅发送一次消息),但数据会丢失。
至少一次(At Least Once),此时ack=-1且分区副本大于等于2且ISR里应答的最小副本数量大于等于2可能会出现至少一次的消息(消息多次发送)。例如Leader节点数据已经落盘,ISR中Follow节点数据未落盘时Leader宕机。此时会重发消息,Leader中消息重复。可以保证消息不丢,但不能保证消息不重复。
精确一次(Exactly Once),在至少一次基础上加幂等性计算。重复发送的消息会丢弃。可以保证消息不丢,且能保证消息不重复。
2.4Kafka生产者如何保证发送消息的有序
影响生产者持久化消息顺序性的参数有两个,retries(重试次数)、max.in.flight.request.per.connection(生产者在收到kafka响应之前可以投递多少个消息到kafka实例中)。
当retries>0且max.in.flight.request.per.connection>1会产生顺序性问题。例如前一批次数据写入失败,重试完成前,后一批次消息先写入成功。那么消息顺序性就产生问题。在需要消息强顺序性时最佳实践是配置为retries>0且max.in.flight.request.per.connection=1(牺牲吞吐量,保证消息顺序)