溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Kafka生產(chǎn)者與可靠性保證ACK的方法有哪些

發(fā)布時間:2021-10-18 16:20:14 來源:億速云 閱讀:138 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“Kafka生產(chǎn)者與可靠性保證ACK的方法有哪些”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

生產(chǎn)者消息發(fā)送流程

消息發(fā)送的整體流程,生產(chǎn)端主要由兩個線程協(xié)調(diào)運行。分別是main線程和sender線程(發(fā)送線程)。

在Kafka(2.6.0版本)源碼中,可以看到。

源碼地址: 
kafka\clients\src\main\java\org.apache.kafka.clients.producer.KafkaProducer.java

測試入口:
KafkaProducerTest.testInvalidGenerationIdAndMemberIdCombinedInSendOffsets()

在創(chuàng)建KafkaProducer時,在430創(chuàng)建了一個Sender對象,并且啟動了一個IO線程。

Kafka生產(chǎn)者與可靠性保證ACK的方法有哪些

this.errors = this.metrics.sensor("errors");
this.sender = newSender(logContext, kafkaClient, this.metadata);
String ioThreadName = NETWORK_THREAD_PREFIX + " | " + clientId;
this.ioThread = new KafkaThread(ioThreadName, this.sender, true);
this.ioThread.start();
interceptor

interceptor的作用是實現(xiàn)消息的定制化,類似:spring Interceptor 、MyBatis的插件、Quartz的監(jiān)聽器。

@Override
public Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback) {
     // intercept the record, which can be potentially modified; this method does not throw exceptions
     ProducerRecord<K, V> interceptedRecord = this.interceptors.onSend(record);
    return doSend(interceptedRecord, callback);
}

可通過實現(xiàn)org.apache.kafka.clients.producer.ProducerInterceptor接口開發(fā)自定義器。

簡單自定義例子:

public class CustomInterceptor implements ProducerInterceptor<String, String> {
    // 發(fā)送消息時觸發(fā)
    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        System.out.println("發(fā)送消息時觸發(fā)");
        return record;
    }

    // 收到服務(wù)端的ACK時觸發(fā)
    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
        System.out.println("消息被服務(wù)端接收");
    }

    @Override
    public void close() {
        System.out.println("生產(chǎn)者關(guān)閉");
    }

    // 用鍵值對配置時觸發(fā)
    @Override
    public void configure(Map<String, ?> configs) {
        System.out.println("configure...");
    }
}

// 生產(chǎn)者中添加
List<String> interceptors = new ArrayList<>();
interceptors.add("com.freecloud.plug.kafka.interceptor.CustomInterceptor");
props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, interceptors);
序列化
byte[] serializedKey;
try {
    serializedKey = keySerializer.serialize(record.topic(), record.headers(), record.key());
} catch (ClassCastException cce) {
    throw new SerializationException("Can't convert key of class " + record.key().getClass().getName() +
            " to class " + producerConfig.getClass(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG).getName() +
            " specified in key.serializer", cce);
}
byte[] serializedValue;
try {
    serializedValue = valueSerializer.serialize(record.topic(), record.headers(), record.value());
} catch (ClassCastException cce) {
    throw new SerializationException("Can't convert value of class " + record.value().getClass().getName() +
            " to class " + producerConfig.getClass(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG).getName() +
            " specified in value.serializer", cce);
}

Kafka生產(chǎn)者與可靠性保證ACK的方法有哪些

在kafka針對不同的數(shù)據(jù)類型做了相應(yīng)的序列化工具。如需自定義實現(xiàn)org.apache.kafka.common.serialization.Serializer接口。

路由器(分區(qū)器)
int partition = partition(record, serializedKey, serializedValue, cluster);
消息累加器
RecordAccumulator.RecordAppendResult result = accumulator.append(tp, timestamp, serializedKey,
                    serializedValue, headers, interceptCallback, remainingWaitMs, true, nowMs);

// RecordAccumulator本質(zhì)是一個ConcurrentMap:

private final ConcurrentMap<TopicPartition, Deque<ProducerBatch>> batches;

一個partition一個Batch。batch滿了之后,會喚醒Sender線程發(fā)送消息。

if (result.batchIsFull || result.newBatchCreated) {
    log.trace("Waking up the sender since topic {} partition {} is either full or getting a new batch", record.topic(), partition);
    this.sender.wakeup();
}

數(shù)據(jù)可靠性保證ACK

生產(chǎn)者發(fā)送一條消息到服務(wù)器如何確保服務(wù)器收到消息?如果在發(fā)送過程中網(wǎng)絡(luò)出了問題,或者kafka服務(wù)器接收的時候出了問題,這個消息發(fā)送失敗了,生產(chǎn)者是不知道的。

所以kafka服務(wù)端需要使用一種響應(yīng)客戶端的方式,只有在服務(wù)端確認以后,生產(chǎn)者才發(fā)一下條消息,否則重新發(fā)送數(shù)據(jù)。

那什么時候才算接收成功?因為消息存儲在不同的broker里,所以是在寫入到磁盤之后響應(yīng)生產(chǎn)者。

服務(wù)端響應(yīng)策略

在分布式場景中,只有一個broker寫入成功還是不夠的,如果有多個副本,follower也要寫入成功才行。

服務(wù)端發(fā)送ACK給生產(chǎn)者一般有以下幾種策略。

  1. 只要leader成功接收就可以,會產(chǎn)生副本與leader不一致情況,如果leader出問題可能會出現(xiàn)數(shù)據(jù)丟失風(fēng)險??蛻舳说却龝r間最短。

  2. 需要半數(shù)以上的follower節(jié)點完成同步,這種方式客戶端等待的時間比上邊稍長一點,但可以確保大部分場景不出問題。

  3. 需要所有follwer全部完成同步,客戶端等待時間最長,但如果節(jié)點掛掉的影響相對來說最小,因為所有節(jié)點的數(shù)據(jù)都是完整的。

kafka的ACK應(yīng)答機制就使用了以上三種方式??梢酝ㄟ^配置acks參數(shù)進行配置。

ISR (in-sync replica set)

上邊第三種方式如果保證所有follower同步數(shù)據(jù)成功?

假設(shè)leader接收到數(shù)據(jù),所有follower都開始同步數(shù)據(jù),但是有一個follower出了問題,沒辦法從leader同步數(shù)據(jù),按這個規(guī)則,leader就要一直等待,無法返回ack,成了害群之馬。

所以我們該如果解決這個問題呢?接下來我們把規(guī)則修改一下,不是所有follower都有權(quán)利讓leader等待,而是只有那些正常工作的follower同步數(shù)據(jù)的時候才會等待。

把那些正常和leader保持同步的副本維護起來,放到一個動態(tài)set里,這個就叫做in-sync replica set (ISR)。只要ISR里面的follower同步完數(shù)據(jù)之后,就可以給客戶端發(fā)送ACK。

對于經(jīng)常出問題的follower可以設(shè)定replica.lag.time.max.ms=30(默認30秒),如果超過配置時間才會從isr中剔除。

參數(shù)說明
acks = 0Producer不等待broker的ack,brokder一接收到還沒寫入磁盤就返回,當(dāng)brokder故障時有可能丟失數(shù)據(jù);
acks = 1Producer等待brokder的ack,partition的leader成功落盤后返回ack,如果在follower同步成功前l(fā)eader故障,將會丟失數(shù)據(jù);
acks = -1producer等待brokder的ack,partition的leader和follower全部成功落盤后才返回ack;

以上三種機制性能依次遞減(producer吞吐量降低),數(shù)據(jù)健壯性則依次遞增。實際開發(fā)中可根據(jù)不同場景選擇不同的策略。

“Kafka生產(chǎn)者與可靠性保證ACK的方法有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI