1. 发送消息主要步骤
ProduceRecord对象包含了目标主题和消息内容,也可以额外指定消息键或者分区。调用send()方法后,序列化器首先将消息和键序列化成字节数组,随后将数据传递给分区器。分区器的默认分区策略是:轮询+按键哈希映射。确认分区之后,消息被加入到该分区的一个批次之中,后台的Sender线程负责将消息批次依次发送到对应的Broker上。当消息被成功提交时,Broker会将其偏移量返回;否则,返回错误,生产者在收到错误之后可以选择进行重试。
2. 发送消息
生产者的生产请求必须发送给首领副本,否则会收到“非分区首领”的错误,此时生产者需要再次发起Metadata元数据请求,获取集群信息。
生产者KafkaProducer对象有三种发送消息的方式:
-
fire-and-forget(发送并忘记)
调用send()方法并忽略返回的Future,虽然生产者会自动重试,当也可能会丢失一部分消息。 -
同步发送:
send()之后,调用Futrue.get()进行等待。 -
异步发送—推荐
为send()方法指定回调函数,并在回调函数里处理异常。
Kafka生产者是线程安全的,可采用单例+多线程的方是提高吞吐量,如果还不够,可增加生产者数量。
见Broker章节,Kafka如何处理生产请求。
3. 生产者核心配置参数
bootstrap.servers
Kafka集群的Broker地址清单,无需指定所有Broker,客户端会在启动时发起Metadata请求到任意Broker获取整个集群的Broker信息。
acks
发送确认参数,只有当消息被写入所有同步副本时才被认为是已提交,但生产者可以选择接受不同类型的确认,该参数会导致生产者和Broker之间对已提交存在歧义。
- acks=0,只要消息网络发送成功,生产者就认为消息被成功提交,吞吐量最高;
- acks=1,首领副本收到消息并成功写入活跃片段(不一定刷盘),生产者就认为消息被成功提交,吞吐量取决于发送方式:同步、异步;
- acks=all,所有同步副本收到消息并成功写入,生产者才认为消息被成功提交。配合min.insync.replicas参数(最小同步副本数,Broker参数),就可以确定在返回确认前,至少有多少个副本能够收到消息。这种确认参数可靠性最高。
retries—可能导致分区乱序、消息重复
生产者遇到临时性错误时的重试次数。在需要至少一次语义,不允许消息丢失时,可重试错误尽量交给Kafka自动完成,业务代码里只处理非可重试错误以及重试次数超出的情况。
max.in.flight.requests.per.connection参数表示生产者在收到Broker上一个响应之前可以发送多少个消息批次。当该参数大于1时,重试可能导致分区消息乱序。譬如:第一个批次提交失败,第二个批次提交成功,而后上一个批次重试成功,两个批次的顺序就反过来了。
linger.ms、batch.sise
批次等待时长、批次大小,设置较大的值会增加延迟,但也会提高吞吐量。
4. 分区策略
分区是Kafka实现伸缩性、提供负载均衡能力的解决方案,提高了主题生产、消费的并行度。
在使用Kafka时,我们希望能够将数据、负载均匀的分配到所有的机器上,避免数据倾斜,这隐含了两个前提:
- 分区、首领副本、跟随者副本均匀的分布在Kafka集群上;
- 消息被均匀地生产到主题的分区上。
第一条由Kafka Broker的分区分配策略保证。在创建主题时,Kafka会先首先在不同的Broker上分配首领副本,随后在依次将跟随者副本分配在不同的Broker上。
第二条则由生产者的分区策略保证,其决定了消息被发送到主题的哪个分区。Kafka提供了多种分区策略,常见的有:
轮询策略
具有非常优秀的负载均衡表现。
随机策略
均匀分布能力略逊与轮询。
按键哈希映射
键有两个用途:一是作为消息的额外信息,二是可以决定消息被写道主题的哪个分区。在分区数量不变的情况下,具有相同键的消息会被生产到同一个分区。
自定义分区策略
实现Partitioner接口。
Kafka生产者默认的分区策略是:轮询+按键哈希映射。
5. TCP连接
Kafka的所有通信都是基于TCP协议。在创建KafkaProducer实例时,后台的Sender会创建同所有Broker的TCP连接(通过Metadata请求可获取所有Broker信息),长时间空闲的连接会被关掉。建议使用懒加载进行TCP连接创建。
采用TCP的一个原因是:基于TCP可以实现多路复用请求,将多个数据流合并到底层一旦物理连接中。??