1 kafka中的消息写入与自定义分区器
- 在我们将消息写入kafka的topic时,我们可以通过FlinkkafkaPartitioner指定写入topic的哪个分区。在不指定的情况下,默认的分区器会将每个数据任务映射到一个单独的kafka分区中,即单个任务的所有记录都会发往同一分区。
- 如果任务数多余分区数,则每个分区可能会包含多个任务发来的记录。
- 而如果任务数小于分区数,则默认配置会导致有些分区收不到数据。
- 若此时恰好有使用事件时间的Flink应用消费了该Topic,那么可能会导致问题;
(理由:在上面我们讲到分配器会在每个分区上定义水位线,然后再对各个分区之间的水位线进行合并)
2 Flink的分区数定义多少个合适?
-
Flink kafka 连接器会以并行的方式获取事件流。每个并行的数据源任务都可以从一个或多个分区中读取数据
因此当我的程序并发数比较多的情况下,kafka分区数我也设置多点,是不是就意味着我程序处理kafka中topic的数据就会比较高效?分区设置的多点好还是不好?
分区设置的多点既有好处也有坏处,虽然我的分区设置多点,集群的吞吐量会变大,但每个分区也有自己的开销;… …
3 FlinkkafkaProducer下的消费保障
注意:
这里是通过FlinkkafkaProducer将数据发送到kafka;跟下面的检查点是不一样的
FlinkkafkaProducer下的消费保障总共分为3级别val kafkaSink = new FlinkKafkaProducer[ResultDt]("topicName", kafkaPro, FlinkKafkaProducer.Semantic.EXACTLY_ONCE)
- at most once:对于一条message,kafka的receiver最多收到一次(0次或1次)
(sender把message发送给receiver后,无论receiver是否收到message,sender都不再重新发送message) - at least once(这是默认选项):对于一条message,Kafka的Receiver最少收到一次(1次及以上)
(sender把message发送给receiver后,当receiver在规定的时间内没有恢复或恢复了error信息,那么sender就会重发,知道sender收到receiver的成功信息) - exactly once:对于一条message,receiver确保只收到一次
4 代码
以下样例代码采用的是flink1.7版本,并依托于数据中的key做分区。
class MySchema extends KeyedSerializationSchema[(String, Int)] {
override def serializeKey(element: (String, Int)): Array[Byte] = {
element._2.toString.getBytes() //序列胡key
}
override def serializeValue(element: (String, Int)): Array[Byte] = {
element._1.getBytes() //序列化value
}
override def getTargetTopic(element: (String, Int)): String = {
null //这里返回要发送的topic名字,没什么用,可以不做处理
}
}
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()
}