Flink Sink Kafka 自定義Topic與自定義分區

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章