一、 重要参数
1.1 serializer
为什么需要这serializer? 因为broker 端接收的消息必须以字节数组(byte [] )的形式存在。
常见值:
- org apache kafka.common.serialization.StringSerializer
1.2 client.id
这个参数都说建议显示指定,但也都没说为什么么
这个参数用来设定 KafkaProducer 应的客户端 id 默认值为“” 如果客户端不设置, KafkaProducer自动生成一个非空字符串,内容形式如“producer-I ”“producer -2 ,即字符串“producer-"与数字的拼接。
1.3 retries
KafkaProducer 一般会发生两种类型的异常 可重试的异常和不可重试的异常 。常见的可重试异常有
- NetworkException
- LeaderNotAvailableException
- UnknownTopicOrPartitionException
- NotEnoughReplicasException
- NotCoordinatorException
比如 NetworkException 表示网络异常,这个有可能是由于网络瞬时故障而导致的异常,可以通过重试解决;又比如
LeaderNotAvailableException 表示分区的 leader 副本不可用,这个异常通常发生在 leader 副本下线而新的 eader 副本选举完成之前,重试之后可以重新恢复。
不可重试的异常:RecordTooLargeException,这种异常直接抛出异常,不会重试。
当然必须配置重试次数,默认是0,即使发生可重试异常,也没有作用
和他配合的另外一个参数retry backoff.ms
两次重试的间隔,这个也很重要,应该根据实际业务来
1.4 ack
参考博客link
- ack=1 leader副本成功写入消息代表发送成功。 消息丢失的场景,比如1.发送途中发现
LeaderNotAvailableException
,如果就一个副本,或者正在选举中,这个时候也没有重试机制,那么就会发生丢失 2. 成功写入leader副本,但是这个时候发生leader副本宕机,这个时候ISR 副本还没有进行消息同步,也会发生消息丢失。 - ack=0,无需确认,最大吞吐量设置
- ack=-1 所有ISR副本都写入成功,最强可靠性,但是也不是不会丢消息,后面会补充
1.5 kafka消息大小参数
Kafka 消息大小相关参数设置,这篇博客讲解的非常详细。
- max.request.size :默认是1MB 限制发送请求大小
- message.max.bytes:这个是ProducerBatch大小
个人感觉kafka的关于消息大小的参数太复杂,分为消费者端、服务这段、Broker端,如果没有把握,千万不要乱动
二、 原理分析
2.1 主线程
主线程主要是把消息进行进行编排,为批量发送做准备。
可能经过的步骤
- 拦截器
- 序列化器
- 分区器
必经过的步骤就是在消息累加器中进行编排
编排的规则是按照topic的分区进行的,每个分区用双端队列进行存储,这也是kafka并不是完全的消息有序,只是分区消息有序的原因,进过编排后形成一个批次 ProducerBatch
,每个 ProducerBatch
是由ProducerRecord
组成,既然设计了消息消息累加器,那么必定为有参数控制这个大小,他就是buffer.memory
(默认32M). 如果主线程的速度太快,sender线程完全跟不上,这个时候消息累加器已经会满,这个时候有两个选择抛出异常和等待sender线程消费,这个是有max.block.ms
参数来控制的
重要参数batch.size
ProducerBatch 大小和 batch size 参数也有着密切的关系。当一条消息(ProducerRecord ) 流入 RecordAccumulator 时,会先 找与消息分区所对应的双端队列(如果没有则新建),再从这个双端队列的尾部获取一个 ProducerB atch (如果没有则新建),查看 ProducerBatch 中是否还可以写入这个 ProducerRecord ,如 果可以 写入,如果不可 以则 需要 建一个新Producer Batch 。在新建 ProducerBatch 评估这条消息的大小是否超过 batch size 参数大小,如果不超过,那么就以 batch size 参数的大 来创建 ProducerBatch ,这样在使用完这段内存区域之后,可以通过 BufferPool 的管理来进行 用;如果超过,那么就以评估的大小来ProducerBatch 这段内存区域不会被复用。
也就是说ProducerBatch
大小不是固定的,但是如果大小是batch.size
,那么会更高效,因为会复用
2.2 sender线程
比较特殊的是第7步骤InFlightRequest
,这个就是控制kafka异步发送消息的ack机制,如果ack=0或者-1的时候,是需要确认的,这个时候是根据node节点(也就是broker节点)进行区分,每个node节点都会把request进行编排,数量由max.in.flight.requests.per. connection
(默认是5)来控制的,超过这个数量的request就会等待发送直到前面5个有了响应并删除的时候,编排好之后,就可以发送了,如果哪个发送成功了会向InFlightRequest
进行确认,然后删除掉已经响应成功的。
三、 kafka完全顺序
Kafk 可以保证同一个分区中的消息是有序的。如果生产者按照一定的顺序发送消息,那么这些消息也会顺序地写入分区,进而消费者也可以按照同样的顺序消费它们。对于某些应用来说,顺序性非常重要 ,比如 MySQL binlog 输,如果出现错误就会造成非常严重的后果果将 acks 参数配置为非零值,并且 max.flight.requests.per.connection 参数配置为大于1 的值,那么就会出现错序的现象 如果第一批次消息写入失败 而第二批次消息写入成功,那么生产者会重试发送第一批次的消息, 此时如果第一 次的消息写入成功,那么这两个批次的消息就出现了错序 一般而言,在需要保证消息顺序的场合建议把参数 max .flight.requests per connection 配置为1 ,而不是把 acks=0 配置为这样也会影响整体的吞吐。
总结下来就是
- 分区数量设置为1
- max.in.flight.requests.per.connection设置为1
重点补充一下
如果设置打于1,那么每次重试可能存在从新排序(乱序)的情况,比如我设置为5,那么我传输的过程应该是1,2,3,4,5 但是如果网络波动导致3丢失,那么kafka自动重试只会拉取3,也就是排序就成了1,2,4,5,3
所以,记得将每个批次的消息数设置为1,防止出现重试乱序的情况.