本文以以下代码所示的实例展开producer的相关讲解
如上图所示,一个正常的生产者逻辑需要最少包含3个逻辑
1.创建生产者实例。
2.构建需要发送的消息。
3.发送消息。
在发送消息的时候我们使用ProducerRecord对象,该对象的基本属性如下所示
topic代表要发送的主题,partition代表要发送的分区编号,headers代表消息的头,key代表消息的键,value代表消息的值,timestamp代表消息的时间戳。
一般情况下可以只显式指定的topic和value,headers代表消息的头部,可以在此域添加一些附加信息。key的用处比较多,例如可以用来计算partition,这样可以让消息发往特定的分区。同一个key的消息会被追加到同一个分区,可以借此功能来对业务进行定制化处理。另外有key的消息还可以支持日志的压缩功能。
接下来我们返回producer实例,对此进行讲解
1.bootstrap.servers: 生产者客户端连接kafka集群所需要的broker实例,具体格式是host1:port1,host2:port2,可以设置一个或者多个地址,如果设置一个,那么producer client会从给定的broker信息中查到其他的broker信息,不过这样设置有一个缺点,当设置的这个broker宕机之后,整个生产者不能用,一般建议配置多个,最少两个。
2.key.serializer: broker接收的消息都是以字节数组的形式存在,在消息发送到broker之前会将消息中的key和value进行相应的序列化操作,key.serializer就是用来指定消息的key对应的序列化器,次参数无默认值,也就是说必须配置,并且必须为全限定类名(只指定类名无效)。
3.value.serializer: 参考key.serializer的定义。
4.client.id: 用来设定producer对应的客户端id,有默认值,一般默认值为producer-1,producer-2等,就是producer-与数字的拼接。
对于别的参数可以在以后的文章中继续讲解。
kafkaproducer是线程安全的,也就是说多个线程可以共享一个kafka实例,那么可以将producer实例进行池化供其他线程调用。
消息发送有三种模式:
发后即忘: 发后即忘就是只管往kafka中发送消息,不管消息的状态,只要发送就行,例如上例中的直接调用producer.send()。一般情况下这种发送也没问题,不过总是会有特殊情况比如发生不可重试异常的时候,消息会被直接丢掉,造成消息丢失,性能和可靠性往往成反比,这种模式性能最高,但是安全性最差。
同步发送: 可以看看kafka的send方法,具体如下:
到这里应该比较清楚了,同步方式无非是利用future.get()阻塞住直到producer.send()返回结果。
异步发送: 如上图所示,异步方式也是利用future的特性,实现了callback回调,要注意的是callback回调方法是在kafkaproducer的主线程完成的,所以要避免callback方法中有阻塞或者耗时的实现,另外对于同一个partition而言,回调也是有序的,即消息1在消息2之前发送,那么kafka可以保证callback1在callback2前面执行。
消息在send的过程中可能需要经过拦截器,序列化器和分区器的一系列作用后才能被发送给broker,这三个组件都可以自定义实现。下面一一进行讲解。
拦截器: 拦截器拦截器一般作用都类似,在执行某个逻辑之前插入一段逻辑,可以利用此特性做一些消息发送前的准备工作,包括过滤消息,修改消息,监控消息,统计消息等一系列逻辑。这个完全根据自身的需求去适配。自定义拦截器实现org.apache.kafka.clients.producer.ProducerInterceptor接口,在这个接口里面可以拿到消息的所有信息,包括消息的topic,partition,key等。一般不建议修改消息相关的信息。
序列化器: producer需要用序列化器把对象转换成字节数组才能通过网络发送给kafkabroker,kafka自身带了类似于String,ByteArray,Double,Long,Integer等序列化器,一般情况下可以满足要求,但是在一些特殊的场景里kafka提供的序列化器无法满足应用需求,这时候也可以自定义序列化器。
自定义序列化器也比较简单,只需要自己实现org.apache.kafka.common.serualization.Serializer接口。并且在propeties里面指定读取自己定义的序列化器就可以,当然有序列化器就有反序列化器,序列化器在producer端,反序列化器在consumer端,两者的序列化和反序列化要能对应上。
分区器: 默认是DefaultPartitioner,大概逻辑为消息key不为null那么会对key进行hash根据hash的结果确定被发往哪个分区,如果key为null,那么会轮训发往topic内的各个可用主题。
在此请注意如果key不为null,那么会发送到全部的partiton中的某一个,不管该分区可不可用,如果key为null,那么会发送到可用分区的某一个。自定义分区器也是只需要实现对应的Parationer接口即可。
至此,消息发送的前置工作已经准备完毕,接下来文章会根据producer的整体架构对producer再进行深入的讲解。