KeyedDeserializationSchema 解析
KeyedDeserializationSchema是flink-kafka-connector(1.7)的一个重要的类,它的作用是将kafka的消息进行反序列化,我们最常用的新建FlinkKafkaConsumer中的SimpleStringSchema就是该类的一个具体实现,作用就是将kafka message的value吐出来供下游消费。
FlinkKafkaConsumer011 consumer = new FlinkKafkaConsumer011<>("topic", new SimpleStringSchema(), properties)
利用这一个自定义很方便的类,我们可以根据我们的业务实现很多具体的功能
从kafka直接读出JSON
虽然flink内部提供了JSONKeyValueDeserializationSchema,但是它采用的是Jackson,而我们生产习惯用fastjson,如果我们不关注kafka消息的metadata,而是关注的是消息体的json,我们就可以这么用,而且可以直接将json吐到下游的算子进行处理,省略了一个map的操作。
public class JsonDeserializationSchema implements KeyedDeserializationSchema <JSONObject> {
@Override
public JSONObject deserialize(byte[] key, byte[] message, String topic, int partition, long offset){
try {
String msg = new String(message, "utf-8");
return JSONObject.parseObject(msg);
}catch (Exception e){
return null;
}
}
@Override
public boolean isEndOfStream(Tuple2 nextElement) {
return false;
}
.......
}
获取kafka message的元信息
有时候如果业务方同时消费多个Topic的时候,他希望知道哪条消息是从哪个Topic过来的;或者该消息对应的offset是多少,用这个值来做监控,我们就可以进行如下的改造。除了来源Topic和对应offset之外,它还可以获取kafka消息的key,甚至是时间戳和分区信息。
public class JsonWithMetaDataDeserializationSchema implements KeyedDeserializationSchema <Tuple2<MessageMetaData, JSONObject>> {
@Override
public Tuple2<MessageMetaData, JSONObject> deserialize(byte[] key, byte[] message, String topic, int partition, long offset){
MessageMetaData metaData = new MessageMetaData();
try {
String msg = new String(message, "utf-8");
metaData.setSourceName(topic);
return new Tuple2<>(metaData, JSONObject.parseObject(msg));
}catch (Exception e){
metaData.setDirty(true);
metaData.setExceptionType(e.getClass().getSimpleName());
return null;
}
}
@Override
public boolean isEndOfStream(Tuple2 nextElement) {
return false;
}
......
}
消费到指定位置自动停止
除了KeyedDeserializationSchema这个类本身的抽象方法deserialize之外,它的父类KafkaDeserializationSchema还有一个方法叫isEndOfStream,这个方法可以用来判断消费流是不是应该终止了。利用这一点,我们可以实现固定消费多少条数据就自动停止任务,这个工具用来测试简直是太好用了,为此我们基于这个CountDeserializationSchema 开发了一个kafka跨集群的同步测试工具叫kafkaDistcp。除了指定条数之外,我们还可以往kafka发送指定格式的消息,然后flink程序消费到这条消息就停止消费,但是注意一点是flink的消费是按照分区来的,所以我们得往每个分区都发一条这样格式的消息,然后判断是否所有分区的“结束”消息都收到了,如果是的话,就停止消费。
public class CountDeserializationSchema implements KeyedDeserializationSchema <Tuple2<MessageMetaData, String>> {
private int maxCount;
private int count;
public CountDeserializationSchema(int maxCount) {
this.maxCount=maxCount;
}
@Override
public String deserialize(byte[] key, byte[] message, String topic, int partition, long offset){
try {
String msg = new String(message, StandardCharsets.UTF_8);
count++;
return msg;
}catch (Exception e){
return null;
}
}
@Override
public boolean isEndOfStream(Tuple2 nextElement) {
return this.count>=maxCount+1;
}
......
}