Kafka源码解读——生产者_KafkaProducer

Kafka源码解读——生产者_KafkaProducer(间断持续更......)

KafkaProducer类的注释里有以下几段:

  • producer是线程安全的,因此多线程使用一个单例模式的对象比多个对象更快些。
  • producer有一个缓冲池用来保存还没有发送到服务器的消息数据,以及一个后台I/O线程负责将这些消息数据转换成一个request请求发送到Kafka集群上,注意发送完成之后一定要close掉producer,否则会造成数据丢失。
  • 发送消息数据(record)的request失败之后,producer会自动重试,将<retries>设置为0之后就不会自动重试。注意:启用之后有几率重复发送。
  • producer为每个分区维护未发送记录的缓冲区,这个缓冲区的大小由<batch.size>定义,值越大批处理单次传输的数据越大,但是受主机内存的限制(每一个活动中的partition都有一个这样的缓冲区)
  • 默认情况下,即使缓冲区内有额外的未使用的空间也会立即send buffer将缓冲区的数据发出去。如果你要减少request的数量可以使用<linger.ms>配置要求值大于等于0,producer将在request之前等待该毫秒值,希望更多记录到达相同批次,这类似于TCP中的Nagle算法。举个例子,在下面的代码中,假如有100个record在一个请求中发送,我们将<linger.ms>设置为1,在缓冲区没有填满的时候,会等待1ms等待更多record到达缓冲区;注意:配置为0,在时间上紧密到达的record通常也会一起批处理,所以无论延迟配置如何,最终都会发生批处理。将<linger.ms>设置为大于0的值可以在不受最大负载影响的情况下以较少的延迟为代价,从而导致更少,更高效的请求。
  • <buffer.memory>配置缓存区的大小,如果records的发送速度大于发送到服务器的速度,缓冲空间会被用完,额外的call将会被阻塞,阻塞的阈值时长由<max.block.ms>配置,超出之后会抛TimeoutException
// 提供一个简单的producer发送消息的样例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
// 控制消息完整性
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

Producer<String, String> producer = new KafkaProducer<>(props);
for(int i = 0; i < 100; i++)
    producer.send(new ProducerRecord<String, String>("my-topic", Integer.toString(i), Integer.toString(i)));

producer.close();

参数

    private static final Logger log = LoggerFactory.getLogger(KafkaProducer.class);
    private static final AtomicInteger PRODUCER_CLIENT_ID_SEQUENCE = new AtomicInteger(1);
    private static final String JMX_PREFIX = "kafka.producer";
    // client.id
    private String clientId;
    private final Partitioner partitioner;
    private final int maxRequestSize;
    private final long totalMemorySize;
    // 存储Kafka元数据
    private final Metadata metadata;
    private final RecordAccumulator accumulator;
    private final Sender sender;
    private final Metrics metrics;
    // i/o线程,用来异步同步数据使用
    private final Thread ioThread;
    private final CompressionType compressionType;
    private final Sensor errors;
    // 系统时间
    private final Time time;
    private final Serializer<K> keySerializer;
    private final Serializer<V> valueSerializer;
    // 生产者配置信息
    private final ProducerConfig producerConfig;
    private final long maxBlockTimeMs;
    private final int requestTimeoutMs;
    private final ProducerInterceptors<K, V> interceptors;

方法

KafkaProducer(ProducerConfig, Serializer<K>, Serializer<V>)

  • 构造方法主要作为初始化producer配置信息,K和V的序列化类。暂时不做解读,有兴趣的请看kafka源码,地址看文末。
  • 注意:producer使用完成之后需要调用close(),避免资源数据泄露。

send(ProducerRecord,Callback)

  • send方法是异步的,调用该方法会将记录数据(record)发到还没发送的记录数据(record)缓冲区,并更新record元数据,确认发送时调用提供回调方法(Callback),优点是:producer可以异步批量的发送单例的消息(record)以提高效率。
  • 返回结果RecordMetadata记录了records发送到的指定分区、偏移量、时间戳。如果Topic使用CreateTime,则时间戳将是提供给用户的时间戳,如果用户没有为记录指定时间戳,则为记录发送时间。如果对Topic使用LogAppendTime,则在添加消息时,时间戳将是Kafka broker主机本地时间。
  • 由于send调用是异步的,所以它返回将分配给该记录的RecordMetadata的未来。在此将来调用get()将阻塞,直到相关请求完成,然后返回记录的元数据,或者抛出发送记录时发生的任何异常。
  • 模拟阻塞代码
 byte[] key = "key".getBytes();
 byte[] value = "value".getBytes();
 ProducerRecord<byte[],byte[]> record = new ProducerRecord<byte[],byte[]>("my-topic", key, value)
 producer.send(record).get();
  • 完全非阻塞使用可以使用回调参数来提供一个回调,该回调将在请求完成时调用。
ProducerRecord<byte[],byte[]> record = new ProducerRecord<byte[],byte[]>("the-topic", key, value);
 producer.send(myRecord,
               new Callback() {
                   public void onCompletion(RecordMetadata metadata, Exception e) {
                       if(e != null) {
                          e.printStackTrace();
                       } else {
                          System.out.println("The offset of the record we just sent is: " + metadata.offset());
                       }
                   }
               });
  • 对于发送到相同分区的记录,保证按顺序执行回调。也就是说,在下面的例子中,callback1保证在callback2之前执行。
producer.send(new ProducerRecord<byte[],byte[]>(topic, partition, key1, value1), callback1);
producer.send(new ProducerRecord<byte[],byte[]>(topic, partition, key2, value2), callback2);
 
  • 当作为事务的一部分使用时,不需要定义回调函数或检查将来的结果来检测发送的错误。如果任何发送调用失败,并且出现不可恢复的错误,则最后一个commitTransaction()调用将失败,并从上次失败的发送中抛出异常。发生这种情况时,应用程序应该调用abortTransaction()重置状态并继续发送数据。

参考: KafkaProducer文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值