kafka-producer配置
kafka-producer版本对比
Kafka的producer的API根据版本的不同分为kafka0.8.1.X之前的
kafka.javaapi.producer.Producer.以及之后版本中出现的org.apache.kafka.clients.producer.KafkaProducer,
建议以后都直接使用org.apache.kafka.clients.producer.KafkaProducer,选择的依据。
1),kafka.javaapi.producer.Producer有丢数情况,而org.apache.kafka.clients.producer.KafkaProducer还没出现过意料之外的丢数。
2),org.apache.kafka.clients.producer.KafkaProducer的性能也比旧的效率高,请参照官网的测试。
由于新版本的优势比较大,在这里不在赘述旧版本的信息。
新版本producer的基本过程
1),生产者客户端应用程序产生消息。
2),链接对象将消息包装到请求中,把消息放入本地的内存队列中,然后由一个消息发送线程从队列拉取消息,
以批量的方式发送到服务端。kafka的记录收集器(RecordAccumulator)负责缓存生产者的消息,发送线程(Sender)
负责记录收集器的批量信息,通过网络发送给服务端。
3),服务端连接对象负责接收请求,将消息以文件形式存储,为了保证客户端网络请求的快速响应,kafka使用选择器(Selector)处理网络连接和读写请求,
使用网络连接(NetworkClient)处理客户端网络请求。
4),服务端响应结果给客户端。
5),确认发送成功,循环发送下个批次。
新版本producer的配置
1),bootstrap.servers
用于配置cluster中borker的host/port对。可以配置一项或者多项,不需要将cluster中所有实例都配置上。因为它后自动发现所有的broker。
如果要配置多项,格式是:host1:port1,host2:port2,host3:port3….
2),key.serializer
配置key序列化类名。如果是自定义的key,那么自定义的key类要实现Serializer接口。
3),value.serializer
配置value序列化类名。如果是自定义的value,那么自定义的value类要实现Serializer接口。
4),acks 可配置项为all, -1, 0, 1,默认值是1
为了确保message record被broker成功接收。Kafka Producer会要求Borker确认请求(发送RecordBatch的请求)完成情况。
对于message接收情况的确认,Kafka Broker支持了三种情形:
a、不需要确认;
b、leader接收到就确认;
c、等所有可用的follower复制完毕进行确认。
可以看出,这三种情况代表不同的确认粒度。在Java Producer Client中,对三种情形都做了支持,上述三种情形分别对应了三个配置项:0、1、-1。其实还有一个值是all,它其实就是-1。
5),buffer.memory 默认值为33554432=32M
producer缓存区大小
producer可以用来缓存数据的内存大小。如果数据产生速度大于向broker发送的速度,producer会阻塞或者抛出异常,以“block.on.buffer.full”来表明,这个配置是默认值是flase,也就是当bufferpool满时,不会抛出BufferExhaustException,而是根据max.block.ms进行阻塞,如果超时抛出TimeoutExcpetion。如果这个属性值是true,则会把max.block.ms值设置为Long.MAX。并抛出异常。
6),compression.type
Kafka提供了多种压缩类型,可选值有4个: none, gzip, snappy, lz4。默认值是none。
7),retries
当一个RecordBatch发送失败时,就会重新改善以确保数据完成交付。该配置设置了重试次数,值范围[0, Integer.Max]。如果是0,即便失败,也不会进行重发。
如果允许重试(即retries>0),但max.in.flight.requests.per.connection 没有设置成1。这种情况下,就可能会出现records的顺序改变的现象。例如:一个prodcuder client的sender线程在一次轮询中,如果有两个recordbatch都要发送到同步一个partition中,此时它们肯定是发往同一个broker的,并且是用的同一个TCP connection。如果出现RecordBatch1先发,但是发送失败,RecordBatch2紧接着RecordBatch1发送,它是发送成功的。然后RecordBatch1会进行重发。这样一来,就出现了broker接收到的顺序是RecordBatch2先于RecordBatch1的情况。
8),batch.size
RecordBatch的最大容量。默认值是16384(16KB)。
当多条消息需要发送到同一个分区时,生产者会尝试合并网络请求。这会提高client和生产者的效率。如果消息体大于这个配置,生产者不会尝试发送消息。发送给kafka的消息包含不同的批次,每批发送给一个分区。批次大小太小的话可能会降低吞吐量。如果设为0,会禁用批处理功能。如果批次设置很大,可能会有些浪费内存,因为我们会预留这部分内存用于额外的消息。
9),client.id
逻辑名,client给broker发请求是会用到。默认值是:”” 没有任何功能性的目的,除了记录和跟踪。
10),connections.max.idle.ms
在配置项的时间之后,关闭空闲的链接,也是在该配置项的时候段内都是空闲的话,就关闭连接。
11),linger.ms
默认值:0,即不延迟。
消息延迟发送的毫秒数,目的是为了等待多个消息,在同一批次发送,减少网络请求。
producer组将会汇总任何在请求与发送之间到达的消息记录一个单独批量的请求。通常来说,这只有在记录产生速度大于发送速度的时候才能发生。然而,在某些条件下,客户端将希望降低请求的数量,甚至降低到中等负载一下。这项设置将通过增加小的延迟来完成–即,不是立即发送一条记录,而是等待其他消息,够成批次再发送,producer将会等待给定的延迟时间以允许其他消息记录发送,这些消息记录可以批量处理。这项设置设定了批量处理的更高的延迟边界:一旦我们获得某个partition的batch.size,他将会立即发送而不顾这项设置,然而如果我们获得消息字节数比这项设置要小的多,我们需要“linger”特定的时间以获取更多的消息。 这个设置默认为0,即没有延迟。设定linger.ms=5,例如,将会减少请求数目,但是同时会增加5ms的延迟。
新版本producer的原理设计图
新版本producer的JAVA代码
1)同步发送代码,异步发布代码,多线程发布代码(合为一体)
import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.log4j.Logger; import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; import com.e_chinalife.monitkafka.utils.LoggerUtil; public class KafkaProducerClient { private static String topics; private static String brokers; private static long sleep=100; private static int records=1000; private static long count=0; private static Logger logs = Logger.getLogger(KafkaProducerClient.class); private static void init() throws Exception { try { Properties props = new Properties(); InputStream defaultIn = null; try { defaultIn = ClassLoader .getSystemResourceAsStream("producer.properties"); props.load(defaultIn); } catch (Exception e) { e.printStackTrace(); } finally { if (defaultIn != null) { defaultIn.close(); } } brokers = props.getProperty("bootstrap.servers"); if (brokers != null) {brokers = brokers.trim();} topics = props.getProperty("topics"); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("init is wrong!", e); } } public static void main(String[] args){ try { sender(); } catch (Exception e) { e.printStackTrace(); } } public static void sender() throws Exception { init(); Map<String, Object> config = new HashMap<String, Object>(); config.put("bootstrap.servers",brokers); //config.put("serializer.class", "kafka.serializer.StringEncoder"); config.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer"); config.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer"); //config.put("partitioner.class", "com.e_chinalife.monitkafka.kafka.ProducerPartitioner");自定义发送规则 config.put("acks", "-1"); // config.put("batch.size", "10000000"); // config.put("linger.ms", 0); ExecutorService service = Executors.newFixedThreadPool(1); service.execute(new Sender(topics, config, sleep, records)); } static class Sender implements Runnable { private ProducerRecord<String, Object> record; private long sleep = 1000; private final KafkaProducer<String, Object> producer; private int records = 0; private String content = null; private String topic; //TSMSAP06VLK,TSMSAP07VLK,TSMSAP08VLK,TSMSAP09VLK private String hostname="TSMSAP09VLK"; public Sender(String topic, Map<String, Object> conf, long sleep, Integer records) { this.topic = topic; this.producer = new KafkaProducer<String, Object>(conf); this.sleep = sleep; this.records = records; } private static class proBack implements Callback{ @Override public void onCompletion(RecordMetadata metadata, Exception exception) { if(exception != null){ logs.info("the offset of metadata is :"+metadata.offset()); logs.info(exception.getMessage(), exception); } logs.info("it is completed"); } } private void logDelay(String logMsg,String topic){ Logger logger = LoggerUtil.getDelayLogger(topic); logger.info(logMsg); } @Override public void run() { while (true) { for (int i = 0; i < records; i++) { count++; content =String.format("%s_%s", count,UUID.randomUUID().toString()) ; //this.record = new ProducerRecord<String, Object>(topic,this.hostname, content); this.record = new ProducerRecord<String, Object>(topic, content); int kee=0; try { kee=producer.send(record,new proBack()).get().partition();//异步发送 //producer.send(record);//同步发送 } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(kee); //producer.send(record); logDelay(content, topic); } try { Thread.sleep(sleep); } catch (InterruptedException e) { e.printStackTrace(); } } } } }