Kafka學習(四)-------- Kafka核心之Producer

獨孤風發表於2019-08-06

通過https://www.cnblogs.com/tree1123/p/11243668.html 已經對consumer有了一定的瞭解。producer比consumer要簡單一些。

一、舊版本producer

0.9.0.0版本以前,是由scala編寫的舊版本producer。

入口類:kafka.producer.Producer

程式碼示例:

Properties properties = new Properties();
        properties.put("metadata.broker.list", "kafka01:9092,kafka02:9092");
        properties.put("serializer.class", "kafka.serializer.StringEncoder");
        properties.put("request.requird.acks", "1");
        ProducerConfig config = new ProducerConfig(properties);
        Producer<String, String> producer = new Producer<String, String>(config);
        KeyedMessage<String,String> msg = new KeyedMessage<String,String>("topic","hello");
        Producer.send(msg);

舊版本是同步機制,等待響應。吞吐性很差。在0.9.0.0版本以後,正式下架了。

舊版本的方法:

send   傳送
close   關閉
sync   非同步傳送  有丟失訊息的可能性

二、新版本producer

舊版本producer由scala編寫,0.9.0.0版本以後,新版本producer由java編寫。

新版本主要入口類是:org.apache.kafka.clients.producer.KafkaProducer

常用方法:

send  實現訊息傳送主邏輯
close  關閉producer   
metrics  獲取producer的實時監控指標資料 比如傳送訊息的速率

Kafka producer要比consumer設計簡單一些,主要就是向某個topic的某個分割槽傳送一條訊息。partitioner決定向哪個分割槽傳送訊息。使用者指定key,預設的分割槽器會根據key的雜湊值來選擇分割槽,如果沒有指定key就以輪詢的方式選擇分割槽。也可以自定義分割槽策略。

確定分割槽後,producer尋找到分割槽的leader,也就是該leader所在的broker,然後傳送訊息,leader會進行副本同步ISR。

producer會啟兩個執行緒,主執行緒封裝ProducerRecord類,序列化後發給partitioner,然後傳送到記憶體緩衝區。

另一個I/O執行緒,提取訊息分batch統一傳送給對應的broker。

示例程式碼:

Properties properties = new Properties();
        properties.put("bootstrap.servers", "kafka01:9092,kafka02:9092");
        properties.put("acks", "all");
        properties.put("retries", 0);
        properties.put("batch.size", 16384);
        properties.put("linger.ms", 1);
        properties.put("buffer.memory", 33554432);
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        for (int i = 1; i <= 600; i++) {
            kafkaProducer.send(new ProducerRecord<String, String>("z_test_20190430", "testkafka0613"+i));
            System.out.println("testkafka"+i);
        }
        kafkaProducer.close();

1、構造Properties物件,bootstrap.servers key.serializer value.serializer是必須指定的。

2、使用Properties構造KafkaProducer物件。

3、構造ProducerRecord 指定topic 分割槽 key value。

4、KafkaProducer的send方法傳送。

5、關閉KafkaProducer。

Properties主要引數:

bootstrap.servers 和consumer一樣,指定部分broker即可。而且broker端如果沒有配ip地址,要寫成主機名。

key.serializer value.serializer 序列化引數 一定要全類名 沒有key也必須設定。

acks 三個值

​ 0: producer完全不管broker的處理結果 回撥也就沒有用了 並不能保證訊息成功傳送 但是這種吞吐量最高

​ all或者-1: leader broker會等訊息寫入 並且ISR都寫入後 才會響應,這種只要ISR有副本存活就肯定不會丟失,但吞 吐量最低。

​ 1: 預設的值 leader broker自己寫入後就響應,不會等待ISR其他的副本寫入,只要leader broker存活就不會丟失,即保證了不丟失,也保證了吞吐量。

buffer.memory 緩衝區大小 位元組 預設是33554432 就是傳送訊息的記憶體緩衝區大小 過小的話會影響吞吐量

compression.type 設定是否壓縮訊息 預設值是none 壓縮後可以降低IO開銷提高吞吐,但是會增大CPU開銷。

​ 支援三種: GZIP Snappy LZ4 效能 LZ4 > Snappy > GZIP

retries 傳送訊息重試的次數 預設0 不重試 重試可能造成重複傳送 可能造成亂序

​ retry.backoff.ms 設定重試間隔 預設100毫秒

batch.size 調優重要的引數 batch小 吞吐量也會小 batch大 記憶體壓力會大 預設值是16384 16KB

linger.ms 傳送延時 預設是0 0的話不用等batch滿就傳送 延時的話可以提高吞吐 看具體情況進行調整

max.request.size producer能夠傳送最大訊息的大小 預設1048576位元組 如果訊息很大 需要修改它

request.timeout.ms 傳送請求後broker在規定時間返回 預設30秒 超過就是超時了。

Send方法

fire and forget 就是上邊的示例

Properties properties = new Properties();
        properties.put("bootstrap.servers", "kafka01:9092,kafka02:9092");
        properties.put("acks", "all");
        properties.put("retries", 0);
        properties.put("batch.size", 16384);
        properties.put("linger.ms", 1);
        properties.put("buffer.memory", 33554432);
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        for (int i = 1; i <= 600; i++) {
            kafkaProducer.send(new ProducerRecord<String, String>("z_test_20190430", "testkafka0613"+i));
            System.out.println("testkafka"+i);
        }
        kafkaProducer.close();

非同步回撥 不阻塞

Properties properties = new Properties();
        properties.put("bootstrap.servers", "kafka01:9092,kafka02:9092");
        properties.put("acks", "all");
        properties.put("retries", 0);
        properties.put("batch.size", 16384);
        properties.put("linger.ms", 1);
        properties.put("buffer.memory", 33554432);
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        for (int i = 1; i <= 600; i++) {
            kafkaProducer.send(new ProducerRecord<String, String>("z_test_20190430", "testkafka0613"+i),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());
                         }
                     }           
            });
            System.out.println("testkafka"+i);
        }
        kafkaProducer.close();

同步傳送 無限等待返回

producer.send(record).get()

重試機制

如果需要自定義重試機制,就要在回撥裡對不同異常區別對待,常見的幾種如下:

可重試異常

LeaderNotAvailableException :分割槽的Leader副本不可用,這可能是換屆選舉導致的瞬時的異常,重試幾次就可以恢復
NotControllerException:Controller主要是用來選擇分割槽副本和每一個分割槽leader的副本資訊,主要負責統一管理分割槽資訊等,也可能是選舉所致。

NetWorkerException :瞬時網路故障異常所致。

不可重試異常

SerializationException:序列化失敗異常

RecordToolLargeException:訊息尺寸過大導致。

示例程式碼:

 producer.send(myRecord,
                   new Callback() {
                       public void onCompletion(RecordMetadata metadata, Exception e) {
                           if(e ==null){
                               //正常處理邏輯
                               System.out.println("The offset of the record we just sent is: " + metadata.offset()); 
                               
                           }else{
                                   
                                 if(e instanceof RetriableException) {
                                    //處理可重試異常
                                    ......
                                 } else {
                                    //處理不可重試異常
                                    ......
                                 }
                           }
                       }
                   });
分割槽機制

partitioner決定向哪個分割槽傳送訊息。使用者指定key,預設的分割槽器會根據key的雜湊值來選擇分割槽,如果沒有指定key就以輪詢的方式選擇分割槽。也可以自定義分割槽策略。

對於有key的訊息,java版本的producer自帶的partitioner會根據murmur2演算法計算訊息key的雜湊值。然後對總分割槽數求模得到訊息要被髮送到的目標分割槽號。

自定義分割槽策略:

建立一個類,實現org.apache.kafka.clients.producer.Partitioner介面

主要分割槽邏輯在Partitioner.partition中實現:通過topic key value 一同確定分割槽

在構造KafkaProducer得Properties中設定partitioner.class 為自定義類 注意是全類名

序列化機制

常用的serializer

ByteArraySerializer.class

ByteBufferSerializer.class

BytesSerializer.class

DoubleSerializer.class

IntegerSerializer.class

LongSerializer.class

StringSerializer.class

但是其他一些複雜的就需要自定義序列化:

1、定義資料格式

2、建立自定義序列化類,實現org.apache.kafka.common.serialization.Serializer介面

3、在KafkaProducer的Properties中設定key.serializer value.serializer為自定義類

以上均為單執行緒的情況,但producer是執行緒安全的,單執行緒適合分割槽較少的情況,分割槽較多可以多執行緒但對記憶體損耗較大。

相關文章