Flink Sink Kafka 自定義Topic與自定義分區
需求背景:
Flink處理完成數據後,需要將消息傳給Kafka,爲了避免數據傾斜,根據消息的主鍵進行Hash取模,自定義輸出到對應的Topic,爲了提升從Kafka讀取消息的速度,將相同主鍵的消息存放到同一分區。
解決方案:
Flink-Kafka連接器中有一個可以傳遞序列化類和分區器的構造方法,我們可以重寫這兩個方法實現自定義Topic和自定義分區,具體方法如下:
@Deprecated
public FlinkKafkaProducer010(
String topicId, //Topic名稱
SerializationSchema<T> serializationSchema, //序列化類
Properties producerConfig, //kafka producer的屬性
KafkaPartitioner<T> customPartitioner //自定義分區器
) {
this(topicId, new KeyedSerializationSchemaWrapper<>(serializationSchema), producerConfig, customPartitioner);
}
-
數據準備
主鍵
在消息字段中的下標- 自定義的Topic列表
-
代碼實現
-
自定義序列化
- 實現KeyedSerializationSchema接口
- 構造傳參的構造函數
- 重寫serializeKey方法、serializeValue方法和getTargetTopic方法
public class MySchema implements KeyedSerializationSchema<String> { int primaryKeyIndex ; //主鍵下表 List<Map> topicList ; //Topic列表 String topicName; //取模確定的Topic名稱 public MySchema(int primaryKeyIndex,List<Map> topicList){ this.primaryKeyIndex = primaryKeyIndex; this.topicList = topicList; } @Override public byte[] serializeKey(String element) { //element就是flink流中的真實數據,這裏可以根據主鍵下標得到主鍵的值key,然後下面分區器就根據這個key去決定發送到kafka哪個分區中 topicName = element.split(":")[primaryKeyIndex + 2]; //取出key後要轉成字節數組返回 return topicName.getBytes(); } @Override public byte[] serializeValue(String element) { //要序列化的value,這裏一般就原封不動的轉成字節數組就行了 return element.toString().getBytes(); } @Override public String getTargetTopic(String element) { //這裏返回要發送的topic名字 //主鍵取模 int hashCode = Math.abs(topicName.hashCode()); int totalHashIndex = hashCode % (topicList.size() * 10); //topic列表的下標 int topicIndex = totalHashIndex % 10; //topic的名字 String topic= (String) topicList.get(topicIndex).get("TOPIC"); //打印驗證 System.out.println("topic:"+topic+"+hashCode:"+hashCode+"+totalHashIndex:"+totalHashIndex+"+topicIndex:"+topicIndex); return topic; } }
-
自定義分區器
- 繼承FlinkKafkaPartitioner類
- 重寫partition方法
public class MyPartitioner extends FlinkKafkaPartitioner { @Override public int partition(Object record, byte[] key, byte[] value, String targetTopic, int[] partitions) { //這裏接收到的key是上面MySchema()中序列化後的key,需要轉成string,然後取key的hash值取模kafka分區數量 int part = Math.abs(new String(key).hashCode() % partitions.length); //打印驗證 System.out.println("part:"+part+"+size:"+partitions.length); return part; } }
-
傳入參數
FlinkKafkaProducer010<String> producer = new FlinkKafkaProducer010<String>("destTopic", new MySchema(primaryKeyIndex,topicList), props,new MyPartitioner());
這裏的
destTopic
可以是任意字符串
-
-
方案結果
這裏我定義了兩個Topic,分別是event_topic_1和event_topic_2,每個Topic有3個分區
注意事項:
在實現KeyedSerializationSchema接口時,泛型一定要使用String
。開始時使用Object
報錯,因爲KeyedSerializationSchema實例泛型類型不同,導致不能序列化。
引用:
本文參考了https://blog.csdn.net/qq_24124009/article/details/89505189