《Kafka實戰》之生產者API使用(引數解釋超詳細)

lyzx_in_csdn發表於2018-08-26
package com.lyzx.kafka;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.KafkaException;
import org.apache.kafka.common.errors.AuthorizationException;
import org.apache.kafka.common.errors.OutOfOrderSequenceException;
import org.apache.kafka.common.errors.ProducerFencedException;
import org.apache.kafka.common.serialization.StringSerializer;
import org.junit.Test;

import java.util.Properties;

/**
 * A Kafka client that publishes records to the Kafka cluster.
 * The producer is thread safe and sharing a single producer
 * instance across threads will generally be faster than having multiple instances
 *
 * kafka客戶端用於傳送訊息到kafka叢集
 * producer執行緒安全,多個執行緒共享時效率好於每個執行緒一個producer
 */
public class ProducerTest {
    private static String ips = "192.168.29.164:9092";

    /**
     * producer包含著一個保持沒有傳送記錄的緩衝池也是一個後臺IO執行緒
     * 這個執行緒的任務是轉換把記錄轉換為提交請求並將其傳送到叢集,失敗後如果在使用producer將丟失這些訊息
     * send()方法是非同步的,當呼叫時會把record放入緩衝並立即返回,這允許生產者成批的有效率的傳送
     * 每個topic中的訊息都有一個或多個副本,replica分為leader replica和follower replica,leader表示使用者發過來的訊息本體,follower表示由kafka叢集複製而來的副本
     * 其中有一部分副本(follower)和leader保持完全一致(ISR In-Sync Replica),acks表示ISR中和leader保持完全一致的副本個數
     * acks有3個選項 0,1,all  0表示不管有沒有副本和leader保持一致都返回,1表示只要有一個保持一致就返回,all代表全部的ISR都一致才返回,all這種慢但是很可靠
     * 當然ISR同步數和吞吐量成反比
     *
     * 有時候由於leader和follower選舉導致的瞬間錯誤可能會導致傳送失敗
     * 如果傳送請求失敗則producer會自動重新嘗試,重試次數有retries引數決定,
     * 
     * producer為每個partition維護一個緩衝池(裡面放著為傳送記錄),這個大小由batch.size指定,即當達到這個值時傳送
     * 可以指定這個值大一些,可以處理更多的資料,當然也需要更多的記憶體
     *
     * 預設情況下,呼叫send()會立即傳送,即使緩衝池還沒被用滿,如果你想等待一段時間,則使用linger.ms引數指定等待的時間
     * 向下面這樣,為了等待更多的記錄到大緩衝區則等待1毫秒,注意如果記錄到達時間很接近,即使linger.ms=0也會一起傳送,在很重的
     * 壓力下會忽略這個引數,設定這個引數會以較小的延遲為代價獲取更好的效能
     * 
     * buffer.memory 引數控制著producer緩衝的總大小 
     * 如果呼叫send()方法的速度大於傳送到伺服器的速度則緩衝池會被用光,此時send()會被阻塞,
     * 通過設定max.block.ms(最大的阻塞時間) 超過這個時間會丟擲TimeoutException
     * key.serializer和value.serializer 表示如何把ProducerRecord記錄物件轉換為鍵和值
     *
     */
    @Test
    public void basicTest(){
        Properties props = new Properties();
        props.put("bootstrap.servers",ips);
        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<>("yh1","key_"+i,"v_"+i));
        }

        producer.close();
    }

    /**
     * 從 Kafka 0.11,支援兩種提交模式:冪等模式和事務模式,冪等模式從kafka的至少一次語義而來
     *
     * 事務模式允許使用者傳送訊息到多個 topic/分割槽,為了確保冪等性,enable.idempotence必須設定為true
     * 如果設定了那麼retries引數自動設定為Integer.MAX_VALUE,並且acks預設為all
     * 所以有些app不需要修改程式碼而利用這個特性,為了利用這個特性,用該避免重新傳送,
     * 如果某個應用是冪等的那麼不建議配置retries,因為他將自動是Integer.MAX_VALUE
     * 如果傳送方法返回了一個錯誤,那麼建議停止並檢查訊息
     *
     * 最後producer只能保證在一個會話中是冪等的
     * 為了使用事務提交模式必須設定  transactional.id,如果設定了該引數那麼冪等隨著設定冪等依賴自動啟用
     * 此外,事務中包含的主題應配置為耐久性。特別地 replication.factor 應該大於等於3,min.insync.replica=2
     * 為了確保端到端的可靠consumer必須設定為只讀去提交資訊,
     * transactional.id的目的是為了一個producer的多個會話恢復
     *
     *
     * It would typically be derived from the shard identifier in a partitioned,
     * stateful, application. As such, it should be unique to each producer instance running within a partitioned application.
     * All the new transactional APIs are blocking and will throw exceptions on failure.
     * The example below illustrates how the new APIs are meant to be used. It is similar to the example above,
     * except that all 100 messages are part of a single transaction.
     */
    @Test
    public void basicTest2(){
        Properties props = new Properties();
        props.put("bootstrap.servers",ips);
        props.put("transactional.id", "my-transactional-id");
        Producer<String, String> producer = new KafkaProducer<>(props, new StringSerializer(), new StringSerializer());

        producer.initTransactions();

        try{
            producer.beginTransaction();
            for(int i = 200; i < 300; i++){
                producer.send(new ProducerRecord<>("yh1","k2_"+i,"v2_"+i));
            }
            producer.commitTransaction();
        }catch(ProducerFencedException | OutOfOrderSequenceException | AuthorizationException e) {
            // We can't recover from these exceptions, so our only option is to close the producer and exit.
            System.out.println("ProducerFencedException,OutOfOrderSequenceException,AuthorizationException");
            producer.close();
        }catch(KafkaException e){
            System.out.println("KafkaException ");
            // For all other exceptions, just abort the transaction and try again.
            producer.abortTransaction();
        }catch(Exception e){
            System.out.println("Exception ");
            producer.abortTransaction();
        }
        producer.close();
    }
}

 

相關文章