整体上来说的话,Flink 内部是基于 producer-consumer 模型来进行消息传递的,也正是 producer-consumer 模型的存在,Flink 能够实现完美的反压。要想更好的理解为什么 Flink 可以完美的实现反压,我们首先需要明白 Flink内部的 producer-consumer 模型,理解了模型,自然也就懂了反压。
我会用几张图来展示 Flink的 producer-consumer 模型。
我们以 WC 为例,这里盗用一下别人的图片,感谢,笔芯!
我们可以看到从 ExecutionGraph 转化为物理执行图时,IntermediateResult 和 IntermediateResultPartition 分别转化为 ResultPartition 和 ResultSubPartition ,而 ExecutionEdge 转化为 InputGate 和 InputChannel。至此转化完毕,这些也就是我们今天要讨论的主角。再继续讲解主角之前呢,不知道大家对 task 是怎么运行的还有没有印象(没有的同学可以回顾之前的博客 Flink Job是如何被执行的(后续写了再更新链接地址) ),我们曾经提到过,在 Task 的构造器中构建了 ResultPartition 和 InputGate 以及
network.registerTask(this);
此方法会为每一个 Task 的每一个 ResultPartition 申请一个 BufferPool,同时为每一个 Task 的每个 InputGate 申请一个 BufferPool。
@VisibleForTesting
public void setupPartition(ResultPartition partition) throws IOException {
BufferPool bufferPool = null;
try {
...
partition.registerBufferPool(bufferPool);
...
} catch (Throwable t) {
...
}
...
}
@VisibleForTesting
public void setupInputGate(SingleInputGate gate) throws IOException {
BufferPool bufferPool = null;
int maxNumberOfMemorySegments;
try {
...
gate.setBufferPool(bufferPool);
} catch (Throwable t) {
...
}
}
ResultPartition、ResultSubPartition以及 InputGate、InputChannel究竟都是什么鬼呢?不急,看我信手拈来:
1234.png
试想一下,水流正酣,如果此时将下游 1 号和 2 号水龙头关闭,会发生什么情况,首先被添满的肯定是抽水机 1 号 和 2 号,因为抽水机只进不出,抽水机满了,封闭木桶就不出水了,慢慢的封闭木桶也满了,最后,直接作用于上游水龙头,由物理定律可知,当上游水龙头的水压与木桶对上游水龙头的反压一样时,上游水龙头也就不再放水了。
不过抽水机怎么知道,抽哪个桶的水呢?没错,就是通过管道知道的。
为了更好的理解反压,我们可以上游水龙头类比于图123中的 source,封闭木桶类比于 ResultPartition,封闭木桶 1 号和 2 号 类比于 ResultSubPartition,管道 1 号 和 2 号就可以类比 channel ,而 抽水机 就可以类比inputGates,而 下游水龙头当然就类比于 Keyed Aggregation--->sink。
当下游算子消费比上游算子产生的快时,慢慢的 inputGate‘s buffer 就会满了,然后该ResultSubPartition的 buffer 满了,依次往上推,最后形成反压效果,致使上游算子减慢生产的速度,从而慢慢达到平衡状态
网友评论