流程图如下所示:
初始化消息生产者有3个必选参数:
创建和发送消息的流程示例如下所示:
Properties properties = new Properties();
// 指定broker连接
properties.put("bootstrap.servers", "127.0.0.1:9092");
// 指定key序列化器,这里为字符串序列化器
properties.put("key.serializer", StringSerializer.class.getName());
// 指定值序列化器,这里为字符串序列化器
properties.put("value.serializer", StringSerializer.class.getName());
// 初始化生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
// 定义消息所属主题
String topic = "kafka_api";
// 异步发送消息,并获取发送结果
RecordMetadata recordMetadata = producer.send(new ProducerRecord<>(topic, "message")).get();
// 指定发送key=testKey
RecordMetadata recordMetadata2 = producer.send(new ProducerRecord<>(topic, "testKey", "message")).get();
// 指定发送partition=1,key=testKey
RecordMetadata recordMetadata3 = producer.send(new ProducerRecord<>(topic, 1, "testKey", "message")).get();
在发送过程中,可以指定发送的key和partion,key一方面可以作为附加悉尼下,另一方面可以用于消息路由,这里涉及到Kafka的消息发送路由机制,有三种情况:
如果需要自定义分区策略,可以实现Partitioner接口,重写内部的partition()方法,在初始化生产者的时候放入配置属性中,其中key=“partitioner.class”,示例如:
// 指定自定义分区器,其中PARTITIONER_CLASS_CONFIG="partitioner.class"
properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, new Partitioner() {
@Override
// 分区策略实现
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
return 0;
}
// 清理资源
@Override
public void close() {
}
// 初始化资源
@Override
public void configure(Map<String, ?> configs) {
}
});
producer拦截器(interceptor)使得用户在消息发送前后以及producer回调逻辑前有机会对喜爱做一些定制化需求,kafka允许用户指定多个interceptor按序作用于消息以形成一条拦截链。
定义一个拦截器需要实现ProducerInterceptor接口。
一个拦截器示例定义如下:
class MyInterceptor implements ProducerInterceptor{
// 消息发送前回调
@Override
public ProducerRecord onSend(ProducerRecord record) {
return null;
}
// 消息确认发送成功响应回调
@Override
public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
}
// 清理资源
@Override
public void close() {
}
// 初始化资源
@Override
public void configure(Map<String, ?> configs) {
}
}
定义后,可以在创建生产者之前配置属性interceptor.classes,值为拦截器的权限定名字符串:
properties.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, Arrays.asList(MyInterceptor.class.getName()));
除了上面的必要参数外,还有一些比较核心的参数配置
acks指定必须要有多少哥分区副本收到消息,生产者才认为消息写入成功。有以下几种情况
设置生产者内存缓冲区大小,用户缓存要发送到服务器的消息,如果应用程序发送消息操作速度超过发送到服务器的处理响应速度,可能会导致生产者缓存不足,这时send方法调用会被堵塞或抛异常
默认情况,消息发送不会被压缩,可以通过这个参数指定使用snappy,gzip,lz4等压缩算法来降低网络传输开销和内存开销
当生产者向服务器发送消息失败时,会尝试根据此配置次数进行消息重复,如果超过配置次数仍失败,生产者会放弃重试并返回错误。默认情况,生产者会确保每次重试间隔大于100ms,可以通过retry.backoff.ms修改。
当有多个消息需要被发送到同一个分区时,生产者会将他们放在同一个批次里,该参数指定了一个批次可以使用的内存大小,按照字节数计算,而非消息个数。当批次被填满时,批次里的消息会被发送出去。实际生产者不一定会在批次满后才发消息,因此设置得很大不会导致消息延迟,但会占用更多的内存。如果设置的过小,会导致生产者需要更频繁地发送消息。
指定生产者在发送批次之前,等待更多消息加入批次的时间,生产者会在批次被填满或达到linger.ms时把批次发送出去。可以通过调整linger.ms取到吞吐量和资源开销之间的平衡。
指定生产者在单个批次里能够发送消息的最大值,最好和broker对应的可接受的消息最大值匹配,避免生产者发送的消息被broker拒绝
指定生产者在收到服务器响应前可以发送多少个消息,值越高,则吞吐量越高,但占用内存也越多,同时还会有在消息发送失败时乱序的风险。将值设为1,可以保证消息是按照发送的顺序写入服务器的,即使发生了重试。