Kafka之自定义拦截器

一. 拦截器原理

Producer拦截器(interceptor)和Consumer拦截器是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑。

对于producer而言,interceptor使得用户在消息发送前以及producer回调逻辑前有机会对消息做一些定制化需求,比如修改消息等。同时,producer允许用户指定多个interceptor按序作用于同一条消息从而形成一个拦截链(interceptor chain)。Intercetpor的实现接口是org.apache.kafka.clients.producer.ProducerInterceptor,其定义的方法包括:

  1. configure(configs)
    获取配置信息和初始化数据时调用。
  2. onSend(ProducerRecord):
    该方法封装进KafkaProducer.send方法中,即它运行在用户主线程中。Producer确保在消息被序列化以及计算分区前调用该方法。用户可以在该方法中对消息做任何操作,但最好保证不要修改消息所属的topic和分区,否则会影响目标分区的计算。
  3. onAcknowledgement(RecordMetadata, Exception):
    该方法会在消息从RecordAccumulator成功发送到Kafka Broker之后,或者在发送过程中失败时调用。并且通常都是在producer回调逻辑触发之前。onAcknowledgement运行在producer的IO线程中,因此不要在该方法中放入很重的逻辑,否则会拖慢producer的消息发送效率。
  4. close():
    关闭interceptor,主要用于执行一些资源清理工作,Producer的close()方法执行后运行
    如前所述,interceptor可能被运行在多个线程中,因此在具体实现时用户需要自行确保线程安全。另外倘若指定了多个interceptor,则producer将按照指定顺序调用它们,并仅仅是捕获每个interceptor可能抛出的异常记录到错误日志中而非在向上传递。这在使用过程中要特别留意。

二. 拦截器案例

  1. 案例需求

实现一个简单的双interceptor组成的拦截链。
第一个interceptor会在消息发送前将时间戳信息加到消息value的最前部;
第二个interceptor会在消息发送后更新成功发送消息条数或失败发送消息条数。

  1. 准备工作

1). 使用IDEA工具创建Maven项目,并导入Kafka依赖


        
            org.apache.kafka</groupId>
            kafka-clients</artifactId>
            0.11.0.0</version>
        </dependency>
</dependencies>

2). 确保虚拟机已安装配置Zookeeper和Kafka
3). 启动Zookeeper和Kafka集群

  1. 案例操作

1). 创建时间拦截器类

package com.learn.kafka.Interceptor;

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 TimeInterceptor implements ProducerInterceptor,String> {
    public ProducerRecord,String> onSend(ProducerRecord,String> producerRecord) {
        return new ProducerRecord,String>(producerRecord.topic(),producerRecord.partition(),producerRecord.timestamp(),producerRecord.key(),System.currentTimeMillis()+","+producerRecord.value().toString(),producerRecord.headers());
    }

    public void onAcknowledgement(RecordMetadata recordMetadata, Exception e) {
    }

    public void close() {
    }

    public void configure(Map, ?> map) {
    }
}

2). 创建消息统计拦截器,统计发送消息成功和发送失败消息数,并在producer关闭时打印输出成功/失败的消息条数

package com.learn.kafka.Interceptor;

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 CountIncerceptor implements ProducerInterceptor,String> {
    private long successNum = 0L;
    private long errorNum = 0L;

    public ProducerRecord, String> onSend(ProducerRecord, String> producerRecord) {
        return producerRecord;
    }

    public void onAcknowledgement(RecordMetadata recordMetadata, Exception e) {
        if(e == null){
            successNum+=1;
        }else{
            errorNum+=1;
        }
    }

    //Producer的close()方法执行完毕后再执行拦截器的close()方法
    public void close() {
        System.out.println("发送成功的数量为:"+successNum);
        System.out.println("发送失败的数量为:"+errorNum);
    }

    public void configure(Map, ?> map) {
    }
}

3). 创建producer主程序,配置中加入自定义的拦截器(两种方法)

package com.learn.kafka.Interceptor;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class CustomerProducer2 {
    public static void main(String[] args) {

        //参数配置
        Properties prop = new Properties();
        prop.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"cos100:9092");
        prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
        prop.put(ProducerConfig.ACKS_CONFIG,"all");
        prop.put(ProducerConfig.BATCH_SIZE_CONFIG,16384);
        prop.put(ProducerConfig.LINGER_MS_CONFIG,1);
        //将多个拦截器加入配置的方法有两个
        //1.在配置参数后边添加,用,隔开,如下
        prop.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,"com.learn.kafka.Interceptor.TimeInterceptor,com.learn.kafka.Interceptor.CountIncerceptor");
        //2.将多个拦截器加入一个List集合中,然后将List加入配置中
//        List interceptors = new ArrayList();
//        interceptors.add("com.learn.kafka.Interceptor.TimeInterceptor");
//        interceptors.add("com.learn.kafka.Interceptor.CountIncerceptor");
//        prop.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,interceptors);

        //创建Topic对象
        KafkaProducer, String> producer = new KafkaProducer, String>(prop);

        //调用send方法
        for(int i=0;i<100;i++){
            producer.send(new ProducerRecord, String>("test",i+"","message-"+i));
        }

        //关闭生产者
        producer.close();
    }
}

producer主程序编写完成后开启虚拟机消费者端口等待生产者生产发送数据

[root@cos100 kafka]# bin/kafka-console-consumer.sh --bootstrap-server cos100:9092 --topic test

运行producer主程序,控制台输出如下,说明自定义的消息统计拦截器生效并正常工作。
Kafka之自定义拦截器_第1张图片
消费者端消费到的信息如下,说明自定义的时间拦截器也生效并正常工作,我们的双interceptor组成的拦截链生效。

1582560717345,message-4
1582560717345,message-6
1582560717346,message-10
1582560717346,message-12
1582560717346,message-13
1582560717346,message-14
1582560717346,message-18
1582560717347,message-19
1582560717347,message-20
1582560717347,message-24
...

完毕!

你可能感兴趣的:(大数据,kafka)