Kafka Producer

1. Kafka Producer

0.9.x版本中,社区正式使用Java版本的producer替换了原Scala版本的producer。新版本的producer的主要入口类是org.apache.kafka.clients.producer.KafkaProducer,而非原来的kafka.producer.Producer

新版的producer不再依赖zookeeper,甚至不需要和zookeeper集群进行直接交互,降低了维护成本,也简化了部署producer应用的开销成本。

1.1. Producer概览

Producer的首要功能就是向某个topic的某个分区发送一条消息,所以首先需要确认到底要向topic的哪个分区写入消息,这就是分区器(partitioner)要做的事情。

Kafka Producer提供了一个默认的分区器。对于每条待发送的消息而言,如果该消息指定了key,那么该partitioner会根据Key的哈希值来选择目标分区;若这条消息没有指定key,则partitioner使用轮询的方式确认目标分区,这样可以最大限度地确保消息在所有分区上的均匀性。
当然producer的API赋予了用户自行指定目标分区的权利,即用户可以在消息发送时跳过partitioner直接指定要发送到的分区。另外,producer也允许用户实现自定义的分区策略而非使用默认的partitioner,这样用户可以灵活的根据自身业务需求确定不同的分区策略。

1.2. ProducerRecord

一个ProducerRecord对象封装了一条待发送的消息。ProducerRecord由5个字段构成,分别如下(0.10.2.0):

  • String topic:该消息所属的topic
  • Integer partition:该消息所属的partition
  • K key:消息key,同一个key的消息会被划分到同一个paritition中
  • V value:消息体,一般不为空,如果为空则表示特定的消息(墓碑消息)
  • Long timestamp:消息时间戳

0.11.x版本中引入了Headers headers属性(消息头部),用来设定一些与应用无关的信息。

ProducerRecord允许用户在创建消息对象的时候直接指定要发送的分区,这样producer后续发送该消息时可以直接发送到指定分区,而不用先通过Partitioner计算目标分区。另外,还可以直接指定消息的时间戳,但是一定要慎重使用这个功能,因为它很可能会令时间戳索引机制失效。

1.3. RecordMetadata

RecordMetadata类表示kafka服务器端返回给客户端的消息的元数据信息,包含如下内容:

  • long offset:消息在分区日志中的位移信息。
  • long timestamp:消息时间戳
  • TopicPartition topicPartition:所属topic的partition。
  • long checksum:消息CRC32码
  • int serializedKeySize:序列化后的消息key字节数
  • int serializedValueSize:序列化后的消息value字节数

1.4. 发送消息

创建生产者实例和构建消息实例后,就可以发送(KafkaProducer#send)消息了。
发送消息主要有三种模式:

  • 发后即忘(fire-and-forget)
  • 同步(sync)
  • 异步(async)。

KafkaProducer#send方法本身就是异步的,send方法返回Future类型。

同步发送和异步发送其实就是通过Java的Feture来区分的,调用Futrue#get方法无限等待结果返回,即实现同步发送的效果。
异步发送的方式,一般是在send方法里指定一个CallBack的回调类,实现方法是onCompletion,Kafka在返回响应时调用该回调函数来实现异步的发送确认。使用CallBack的方式非常简洁,kafka有响应时就会回调,要么发送成功,要么抛出异常。
对于同一个分区而言,如果消息record1于record2之前发送,那么kafkaProducer就可以保证对应的callback1在callback2之前调用。也就是说,回调函数的调用可以保证分区有序。

疑问:send方法的返回类型就是Future,而Future本身就可以用作异步的逻辑处理。这样做不是不行,只不过Future里的get方法在何时调用,以及怎么调用都是需要面对的问题,消息不停的发送,那么诸多消息对应的Future对象的处理难免会引起代码处理逻辑的混乱。

KafkaProducer中一般会发生两种类型的异常,可重试异常不可重试异常
所有的可重试异常都继承自org.apache.kafka.common.errors.RetriableException抽象类。理论上未继承自RetriableException类的其他异常都属于不可重试异常。

常见的可重试异常有:NetworkException(网络异常)、LeaderNotAvailableException(分区的leader副本不可用,这个异常发发生在leader副本下线而新的leader副本选举完成之前)、NotControException(controller当前不可用)、UnknownTopicOrPartitionExceptionNotEnoughReplicasExceptionNotCoordinatorException等。
对于可重试异常,如果配置了 retries 参数,默认为0,那么只要在规定的重试次数内自行恢复了,就不会抛异常,否则抛异常。

1.4.1. 工作流程

kafka生产者客户端的整体架构用户首先构建待发送的消息对象ProducerRecord,然后调用KafkaProducer#send方法进行发送。

整个生产者客户端有两个线程协调运行,这两个线程分别主线程(即启动KafkaProducer的线程)和Sender线程(发送线程,自KafkaProducer创建后就一直都在运行着)。

在主线程中由KafkaProducer创建消息,然后通过拦截器(Interceptor)序列化器(Serializer)分区器(Partitioner)的作用之后,将消息追加写入内存中到消息累加器(RecordAccumulator,也称为消息收集器)中。此时KafkaProduer#send方法返回,即send方法仅仅把消息放入消息累加器中。
Sender线程负责从RecordAccumulator中获取消息并将消息发送到对应的broker中,并等待broker发送response回来,完成真正的消息发送逻辑。

消息累加器(RecordAccumulator)主要用来缓冲消息以便Sender线程可以批量发送,进而减少网络传输的资源消耗以提升性能。RecordAccumulator空间的大小可以通过生产者客户端参数buffer.memory配置,默认值为33554432,即32M。如果生产者发送消息的速度超过发送到服务器的速度,则会导致生产者空间不足,这个时候KafkaProducer#send方法调用要么被堵塞,要么抛异常,这个取决于生产者客户端参数max.block.ms的配置,默认值为60000,即60秒。

消息累加器(RecordAccumulator)的内部为每个分区维护了一个双端对列(Deque),队列中的内容就是ProducerBatch。主线程中发过来的消息都会被追加到消息累加器(RecordAccumulator)的某个对列。消息写入RecordAccumulator时,追加到双端对列的尾部,Sender读取消息时,从双端对列的头部读取。
注意:ProducerBatch不是ProducerRecord,ProducerBatch中可以包含一个至多个ProducerRecord。即ProducerRecord是生产者中创建的消息,而ProducerBatch是指一个消息批次,这样可以使字节的使用更加紧凑。

ProducerBatch的大小和生产者参数buffer.size有着密切的关系,默认值是16384,即16KB。
当ProducerBatch满了的时候,Sender会发送该ProducerBatch中的消息。不过Sender并不总是等待ProducerBatch满了才发消息,很有可能当ProducerBatch还有很多空闲空间时就发送该ProducerBatch。这实际上是吞吐量与延时之间的权衡。生产者参数linger.ms及时控制消息发送延时行为的,默认为0,即表示消息需要被立即发送,无须关心ProducerBatch是否已被填满。

从上述过程可以看出新版本producer发送事件完全是异步过程。

2. 参数配置

每个参数都在org.apache.kafka.clients.producer.ProducerConfig中有对应的名称

2.1. 必填参数

bootstrap.servers

key.serializervalue.serializer:broker端接收的消息必须以字节数组(byte[])的形式存在。

2.2. 其他参数

acks
ack参数有3种类型的值,都是字符串类型。

在生产者客户端发送消息的时候将acks参数设置为-1,那么就意味着需要等待ISR集合中的所有副本都确认收到消之后才能正确的收到响应的结果,或者捕获超时异常。
如果在一定的时间内,follower副本没有能够完全拉取到leader的消息,那么就需要返回超时异常给客户端。生产者请求的超时时间由参数request.timeout.ms配置,默认值为30000ms。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值