- 在我們將消息寫入kafka的topic時,我們可以通過FlinkkafkaPartitioner指定寫入topic的哪個分區。
- 在不指定的情況下,默認的分區器會將每個數據任務映射到一個單獨的kafka分區中,即單個任務的所有記錄都會發往同一分區。
- 如果任務數多餘分區數,則每個分區可能會包含多個任務發來的記錄。 而如果任務數小於分區數,則默認配置會導致有些分區收不到數據。
若此時恰好有使用事件時間的Flink應用消費了該Topic,那麼可能會導致問題;
- 導致問題的原因
- Flink_Kafka爲了利用Kafka各個分區的保序性特徵,分配器會在每個分區上定義水位線,然後再對各個分區之間的水位線進行合併。
- 因此,如果某一分區變成非活躍狀態且不再提供消息,那麼這個數據源任務的水位線將無法前進,繼而導致整個應用的水位線都不會前進。因此單個非活躍的分區會導致整個應用停止運行。
- 導致問題的原因
示例:Flink自定義kakfa輸出分區
object addSink_kafka_並自定義序列化和分區 extends App {
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
val streamLocal = StreamExecutionEnvironment.createLocalEnvironment(3)
import org.apache.flink.api.scala._ //如果數據是有限的(靜態數據集)可以引入這個包
val dataStream: DataStream[(String, Int)] = streamLocal.fromElements(("flink_er", 3), ("f", 1), ("c", 2), ("c", 1), ("d", 5))
val properties = new Properties()
properties.setProperty("bootstrap.servers", "master:9092")
val flinkKafkaProducer = new FlinkKafkaProducer(
"ceshi01",
new MySchema(),
properties,
Optional.of(new FlinkKafkaPartitioner[(String, Int)] {
/**
* @param record 正常的記錄
* @param key KeyedSerializationSchema中配置的key
* @param value KeyedSerializationSchema中配置的value
* @param targetTopic targetTopic
* @param partitions partition列表[0, 1, 2, 3, 4]
* @return partition
*/
override def partition(record: (String, Int), key: Array[Byte], value: Array[Byte], targetTopic: String, partitions: Array[Int]): Int = {
Math.abs(new String(key).hashCode() % partitions.length)
}
})
)
dataStream.addSink(flinkKafkaProducer)
streamLocal.execute()
}