生产者详解

发送类型
-
同步发送
//通过send()发送完消息后返回一个Future对象,然后调用Future对象的get方法等待kafka响应 //如果kafka正常响应,返回一个RecordMetadate对象,该对象存储消息的偏移量 //如果kafka发生错误,无法正常响应,就会抛出异常,我们便可以进行异常处理 producer.send(recore).get(); -
异步发送
producer.send(record,new Callback(){ public void onCompletion(RecordMetadate metadata,Exception exception){ if(exception == null){ System.out.println(metadata.partition()+":"+ metadata.offset()); } } })
序列化器
- 消息要到网络上进行传输,必须进行序列化,而序列化器的作用就在此
- kafka提供了默认的字符串序列化器(org.apache.kafka.common.serialization.StringSerializer),
还有整型(IntegerSerializer)和字节数组(BytesSerializer)序列化器.
这些序列化器都实现了接口(org.apache.kafka.common.serialization.Serializer),基本能满足大部分需求 - 也能自定义序列化器.实现Serializer接口即可
分区器
- 本身kafka有自己的分区策略,如果未指定,就会使用默认的分区策略
- kafka根据传递小时的key来进行分区的分配,就会hash(key)%numPartitions.如果key相同的话
,那么就会分配到统一分区
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
int numPartitions = partitions.size();
if (keyBytes == null) {
int nextValue = nextValue(topic);
List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
if (availablePartitions.size() > 0) {
int part = Utils.toPositive(nextValue) % availablePartitions.size();
return availablePartitions.get(part).partition();
} else {
// no partitions are available, give a non-available partition
return Utils.toPositive(nextValue) % numPartitions;
}
} else {
// hash the keyBytes to choose a partition
return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
}
}
- 自定义分区:实现Partitioner接口,然后重写partition方法,在里面写自己分区的逻辑即可
public class DefaultPartition implements Partitioner {
@Override
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
int numPartitions = partitions.size();
return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> configs) {
}
}
拦截器
- Producer拦截器是一个新的功能,他和consumer端interceptor在kafka0.10版本被引入
主要用于是吸纳clients端的定制化控制逻辑 - 生产者拦截器可以用于消息发送前做一些准备工作
- 使用场景
- 按照某个规则过滤掉不符合要求的消息
- 修改消息的内容
- 统计类需求
public class ProducerInterceptorPrefix implements ProducerInterceptor<String,String> {
@Override
public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
// 再这里做消息的处理
return null;
}
@Override
public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> configs) {
}
}
发送原理
- 消息发送的过程中,涉及到两个线程协同工作,主线程首先将业务数据封装成ProducerRecord对象,
之后调用send()方法将消息放入RecordAccumulator(消息收集器,也是主线程和Sender线程直接的缓冲区)
暂存.Sender线程负责将消息信息构成请求,并最终执行网络io的线程,它冲RecordAccumulator取出消息并批量发送出去,
KafkaProducer是线程安全的,多线程共享使用同一个KafkaProducer对象
其他生产者参数
- acks
- acks指定必须有多少个分区副本接收消息,生产者才认为消息写入成功,用户检测数据丢失的可能性
- acks=0:生产者在成功写入消息之前不会等待任何来自服务器的响应。无法监控数据是否发送成功,但可以以网络能够支持的最大速度发送消息,达到很高的吞吐量。
- acks=1:只要集群的首领节点收到消息,生产者就会收到来自服务器的成功响应。
- acks=all:只有所有参与复制的节点全部收到消息时,生产者才会收到来自服务器的成功响应。这种模式是最安全的,
- 如果acks参数配置是整数类型,而不是字符串的话,就会抛出异常

- retries
- 生产者从服务器收到的错误有可能是临时性的错误的次数,如果达到了这个次数,生产者会放弃
重试并返回错误.默认生产者在每次重试之间等待100ms,可以通过retry.backoff.ms参数来修改这个时间间隔
- 生产者从服务器收到的错误有可能是临时性的错误的次数,如果达到了这个次数,生产者会放弃
- batch.size
- 该参数指定了一个批次可以使用的内存大小,按照字节数计算(而不是消息个数)。不能设置太多,会占内存,也不能设置调小,会频繁发送消息
- max.request.size
- 发送的请求大小,broker也有对可接受最大值有自己的限制(message.max.size),两边配置最好一致
来源:CSDN
作者:假装自己不胖
链接:https://blog.csdn.net/weixin_42672777/article/details/103810706

