1.インターセプターの原理
- プロデューサーインターセプター(インターセプター)はKafka 0.10バージョンで導入され、主にクライアント側でカスタマイズされた制御ロジックを実装するために使用されます。
- プロデューサーの場合、インターセプターは、メッセージが送信される前、およびメッセージの変更など、プロデューサーのコールバックロジックの前に、メッセージに対してカスタマイズされた要件を作成する機会をユーザーに提供します。同時に、プロデューサーは、ユーザーが複数のインターセプターを指定して、同じメッセージに順番に作用してインターセプターチェーンを形成できるようにします。
- Intercetporの実装インターフェイスはorg.apache.kafka.clients.producer.ProducerInterceptorであり、その定義されたメソッドは次のとおりです。
(1)configure(configs):構成情報を取得してデータを初期化するときに呼び出されます。
(2)onSend(ProducerRecord):このメソッドはKafkaProducer.sendメソッドにカプセル化されています。つまり、ユーザーのメインスレッドで実行されます。プロデューサーは、メッセージがシリアル化されてパーティションが計算される前に、このメソッドが呼び出されることを確認します。ユーザーはこの方法でメッセージに対して任意の操作を実行できますが、メッセージが属するトピックとパーティションが変更されていないことを確認することをお勧めします。変更しないと、ターゲットパーティションの計算に影響します。
(3)onAcknowledgement(RecordMetadata、Exception):このメソッドは、メッセージがRecordAccumulatorからKafka Brokerに正常に送信された後、または送信プロセスが失敗したときに呼び出されます。そして通常、プロデューサーコールバックロジックがトリガーされる前。onAcknowledgementはプロデューサーのIOスレッドで実行されるため、このメソッドに重いロジックを入れないでください。そうしないと、プロデューサーのメッセージ送信効率が低下します。
(4)閉じる:主にリソースのクリーンアップ作業を実行するために使用されるインターセプターを閉じます。前述のように、インターセプターは複数のスレッドで実行される可能性があるため、ユーザーは特定の実装時に自分でスレッドの安全性を確保する必要があります。さらに、複数のインターセプターが指定されている場合、プロデューサーは指定された順序でそれらを呼び出し、各インターセプターによってスローされる可能性のある例外のみをキャプチャして、上向きに渡すのではなく、エラーログに記録します。使用時には特に注意が必要です。
第二に、インターセプターの場合
1.準備
- IDEでmavenプロジェクトを作成し、pomファイルに依存関係を追加します
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>1.1.1</version>
</dependency>
- zookeeperクラスターを開始します
bin/zkServer.sh start
- kafkaクラスターを開始します
bin/kafka-server-start.sh -daemon config/server.properties
- Kafkaクラスターは消費者を開きます
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic bigdata
--from-beginning
2.需要分析
デュアルインターセプターで構成される単純なインターセプターチェーンを実装します。最初のインターセプターは、メッセージが送信される前にタイムスタンプ情報をメッセージ値の先頭に追加します。2番目のインターセプターは、メッセージの送信後に正常に送信されたメッセージと失敗したメッセージの数を更新します。
3.コード表示
3.1、 增加时间戳拦截器
import org.apache.kafka.clients.producer.ProducerInterceptor;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import java.util.Map;
public class MyTimeInterceptor implements ProducerInterceptor<String,String> {
@Override
public void configure(Map<String, ?> configs) {
}
@Override
public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
/* 取出value值 */
String value = record.value();
/* 创建一个新的 record,把时间戳写入消息体的最前部 */
return new ProducerRecord(record.topic(),record.partition(),record.key(),System.currentTimeMillis() + "---" + value);
}
@Override
public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
}
@Override
public void close() {
}
}
3.2、增加统计次数拦截器,统计发送消息成功和发送失败消息数,并在 producer 关闭时打印这两个计数器
import org.apache.kafka.clients.producer.ProducerInterceptor;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import java.util.Map;
public class MyCountInterceptor implements ProducerInterceptor<String,String> {
private int success;
private int error;
@Override
public void configure(Map<String, ?> configs) {
}
@Override
public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
return record;
}
@Override
public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
/* 统计成功和失败的次数 */
if (metadata != null){
success ++;
}else {
error ++ ;
}
}
@Override
public void close() {
/* 保存结果 */
System.out.println("success: " + success);
System.out.println("error: " + error);
}
}
3.3、producer 主程序
import java.util.ArrayList;
import java.util.Properties;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
public class MyProducerInterceptor {
public static void main(String[] args) {
/* 相当于map操作 */
Properties properties = new Properties();
/* kafka 集群,broker-list */
properties.put("bootstrap.servers", "centos7-1:9092");
/* 等待所有副本节点的应答 */
properties.put("acks", "all");
/* 重试次数 */
properties.put("retries", Integer.valueOf(3));
/* 批次大小 */
properties.put("batch.size", Integer.valueOf(16384));
/* 等待时间 */
properties.put("linger.ms", Integer.valueOf(1));
/* RecordAccumulator 缓冲区大小 */
properties.put("buffer.memory", Integer.valueOf(33554432));
/* key序列化 */
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
/* value序列化 */
properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
/* 构建拦截器链 */
ArrayList<String> interceptors = new ArrayList<>();
interceptors.add("com.jh.interceptor.MyTimeInterceptor ");
interceptors.add("com.jh.interceptor.MyCountInterceptor ");
/* 添加拦截器链 */
properties.put("interceptor.classes", interceptors);
/* 创建生产者对象 */
KafkaProducer<String, String> kafkaProducer = new KafkaProducer(properties);
/* 发送消息 */
for (int i = 0; i < 10000; i++){
/* 发送的主题、key、value */
kafkaProducer.send(new ProducerRecord("bigdata", "jh","jh==" + i));
}
/* 一定要关闭 producer,这样才会调用 interceptor的 close 方法 */
kafkaProducer.close();
}
}
4.操作効果表示