- 在我们将消息写入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()
}