Kafka拦截器
拦截器是早在 Kafka 0.10.0.0 中就引入的一个功能,kafka一共有两种拦截器:生产者拦截器和消费者拦截器。
一、生产者拦截器
生产者拦截器既可以用来在消息发送前做一些准备工作,比如按照某个规则过滤不符合要求的消息、修改消息的内容等,也可以用来在发送回调逻辑前做一些定制化的需求,比如统计类工作
自定义生产者拦截器需实现 org.apache.kafka.clients.producer.ProducerInterceptor 接口,源码如下:
public interface ProducerInterceptor<K, V> extends Configurable { /** * 在将消息序列化和计算分区前调用,来对消息进行相应的定制化操作 * * @param record the record from client or the record returned by the previous interceptor in the chain of interceptors. * @return producer record to send to topic/partition */ public ProducerRecord<K, V> onSend(ProducerRecord<K, V> record); /** * 在消息记录被ack之后或当消息记录发送失败时。 * 通常在用户回调函数被调用之前被调用 * 这个方法运行在Producer的IO线程中,因此这个方法的实现越简单越好,否则会影响消息的发送速度 * * @param metadata * @param exception */ public void onAcknowledgement(RecordMetadata metadata, Exception exception); /** * 在拦截器关闭后调用 * 可用来执行一些资源清理工作 */ public void close(); }
配置生产者拦截器
props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, ProducerInterceptor.class.getName());
二、消费者拦截器
消费者拦截器主要在消费到消息或在提交消费位移时进行一些定制化的操作。
自定义消费者拦截器需实现 org.apache.kafka.clients.consumer.ConsumerInterceptor 接口,源码如下:
public interface ConsumerInterceptor<K, V> extends Configurable, AutoCloseable { /** * 在 KafkaConsumer#poll 方法返回之前调用此方法 * 可以对消息做一些定制化操作,如修改消息内容、按照某种规则过滤消息、生成新的消息记录等 * 如果此方法抛出异常,会被捕获并记录在日志中,但是不会再向上传递 * 若配置了多个拦截器,调用顺序按照 ConsumerConfig#INTERCEPTOR_CLASSES_CONFIG 配置的先后
顺序。后面的拦截器得到的可能是前面拦截器修改后的记录 * * @param records * @return 处理之后的记录,将会传递给消费者或下一个拦截器 */ public ConsumerRecords<K, V> onConsume(ConsumerRecords<K, V> records); /** * 提交offset之后调用此方法 * 可以用来记录跟踪所提交的位移信息 * * @param offsets */ public void onCommit(Map<TopicPartition, OffsetAndMetadata> offsets); /** * 在拦截器关闭后调用 * 可用来执行一些资源清理工作 */ public void close(); }
使用场景:
在某些业务中会对消息设置一个有效期的属性,如果某条消息在既定的时间窗口内无法到达,那么就会被视为无效,它也就不需要再被继续处理了。
消费者拦截器可以用来实现消息的TTL(Time To Live)功能,根据消息的timestamp,将过期的消息过滤掉(放入死信队列中),不再投递给具体的消费者
配置消费者拦截器:
props.put(ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, ConsumerTtlInterceptor.class.getName());
三、过期时间
通过消息拦截器的方式可以过滤处理超时的消息,那超时多久可以认为是超时呢?是固定的时间,还是从消息中去获取超时时间?显然,消息自带超时时间是最好的。
在消息发送时可以将超时时间以键值对的方式保存在消息的headers字段中。这样消费者消费到这条消息的时候可以在拦截器中根据headers字段设定的超时时间来判断此条消息是否超时。
1、发送消息时设置headers
headers字段涉及Headers和Header两个接口,Headers是对多个Header的封装,Header接口表示的是一个键值对。
我们可以直接使用Kafka提供的 org.apache.kafka.common.header.internals.RecordHeaders 和 org.apache.kafka.common.header.internals.RecordHeader。
如发送消息:
// 构造方法:ProducerRecord(String topic, Integer partition, Long timestamp, K key, V value, Iterable<Header> headers) ProducerRecord<Integer, String> producerRecord = new ProducerRecord( "test", // topic null, // 不指定partition System.currentTimeMillis(), // 发送消息的事件戳 null, // key不指定 "这是一条带headers的消息", // msg new RecordHeaders().add(new RecordHeader("ttl", "20".getBytes(StandardCharsets.UTF_8))) ); kafkaTemplate.send(producerRecord);
接收消息:
consumerRecord.timestamp(); Headers headers = consumerRecord.headers(); for (Header header : headers) { header.key(); header.value(); }
END.