我正在尝试Flink并编写以下示例程序:
object IFJob {
@SerialVersionUID(1L)
final class StringInputFormat extends GenericInputFormat[String] {
val N = 100
var i = 0L
override def reachedEnd(): Boolean = this.synchronized {
i >= N
}
override def nextRecord(ot: String): String = this.synchronized {
i += 1
return (i % 2) + ""
}
}
def main(args: Array[String]) {
val env = ExecutionEnvironment.getExecutionEnvironment
val text: DataSet[String] = env.createInput(new StringInputFormat())
val map = text.map {
(_, 1)
}
// map.print()
val by = map.groupBy(0)
val aggregate: AggregateDataSet[(String, Int)] = by.aggregate(Aggregations.SUM, 1)
aggregate.print()
}
}
我创建一次StringInputFormat
并且并行读取(默认并行度= 8)。
当我运行上述程序时,结果在执行之间有所不同,即它们不是确定性的。结果重复1-8次。
例如,我得到以下结果:
// first run
(0,150)
(1,150)
// second run
(0,50)
(1,50)
// third run
(0,200)
(1,200)
预期结果将是
(0,400)
(1,400)
因为StringInputFormat
应该生成8次50" 0"和" 1"记录。
我甚至在输入格式中添加了同步,但它没有帮助。
Flink计算模型中缺少什么?
答案 0 :(得分:1)
您观察到的行为是Flink如何将工作分配给InputFormat
的结果。其工作原理如下:
createInputSplits()
方法,返回InputSplit
数组。 InputSplit
是要读取(或生成)的一大块数据。 GenericInputFormat
为每个并行任务实例创建一个InputSplit
。在您的情况下,它会创建8个InputSplit
个对象,每个InputSplit
应生成50个"1"
和50个"0"
个记录。DataSourceTask
的并行实例是在worker(TaskManagers)上启动的。每个DataSourceTask
都有自己的InputFormat
。DataSourceTask
就会向主人请求InputSplit
,并使用open()
调用InputFormat
的{{1}}方法。当InputSplit
完成处理InputFormat
后,InputSplit
会向主人请求新内容。在您的情况下,每个DataSourceTask
都会得到快速处理。因此,DataSourceTasks之间存在争用,它们的InputFormats请求InputSplits,而一些InputFormats处理多个InputSplit
。由于InputSplit
未重置其内部状态(即设置InputFormat
),因此在打开新i = 0
时,它只会为其处理的第一个InputSplit
生成数据。 / p>
您可以通过将此方法添加到InputSplit
:
StringInputFormat