宏观流程如下图:

client端

生成StreamGraph

  1. env.addSource(new SocketTextStreamFunction(...))
  2. .flatMap(new FlatMapFunction())
  3. .keyBy("word")
  4. .timeWindow(Time.seconds(5))
  5. .reduce(new ReduceFunction())
  6. .print()

StreamExecutionEnvironment上的一系列api调用会在env->transformations中添加相应的StreamTransformation对象,然后调用StreamGraphGenerator->transformation方法遍历所有的StreamTransformation对象生成最终的StreamGraph

如上代码段会生成如下StreamGraph:

StreamGraph->JobGraph

  1. private List<StreamEdge> createChain(
  2. Integer startNodeId,
  3. Integer currentNodeId,
  4. Map<Integer, byte[]> hashes,
  5. List<Map<Integer, byte[]>> legacyHashes,
  6. int chainIndex,
  7. Map<Integer, List<Tuple2<byte[], byte[]>>> chainedOperatorHashes)

StreamGraph的所有sourceStreamNode开始遍历处理,如果是可链接的(isChainabletrue)则继续,同时生成该节点的StreamConfig信息(包含StreamOperator``chainIndex``chainEnd等),否则生成新的JobVertex,最后链接connect函数创建JobEdge对象链接JobVertex

  1. public static boolean isChainable(StreamEdge edge, StreamGraph streamGraph) {
  2. StreamNode upStreamVertex = edge.getSourceVertex();
  3. StreamNode downStreamVertex = edge.getTargetVertex();
  4. StreamOperator<?> headOperator = upStreamVertex.getOperator();
  5. StreamOperator<?> outOperator = downStreamVertex.getOperator();
  6. return downStreamVertex.getInEdges().size() == 1
  7. && outOperator != null
  8. && headOperator != null
  9. && upStreamVertex.isSameSlotSharingGroup(downStreamVertex)
  10. && outOperator.getChainingStrategy() == ChainingStrategy.ALWAYS
  11. && (headOperator.getChainingStrategy() == ChainingStrategy.HEAD ||
  12. headOperator.getChainingStrategy() == ChainingStrategy.ALWAYS)
  13. && (edge.getPartitioner() instanceof ForwardPartitioner)
  14. && upStreamVertex.getParallelism() == downStreamVertex.getParallelism()
  15. && streamGraph.isChainingEnabled();
  16. }

如上代码会生成包含两个JobVertex对象的JobGraph:

JobVertexconfiguration属性中的chainedTaskConfig_``chainedOutputs分别包含了该节点链接的所有StreamNode节点的配置信息和所有SteamNode本身序列化后的二进制数组

JobManager

主要把客户端提交的JobGraph转化成ExecutionGraph,并把ExecutionGraph包含的所有ExecutionVertex对应的Execution提交给分配到其执行所需资源的TaskManager

DistributionPattern分发模式用于确定生产者(产生中间结果IntermediateResultPartition)与其消费者(通过ExecutionEdge)怎样链接

  1. switch (channel.getShipStrategy()) {
  2. case FORWARD:
  3. distributionPattern = DistributionPattern.POINTWISE;
  4. break;
  5. case PARTITION_RANDOM:
  6. case BROADCAST:
  7. case PARTITION_HASH:
  8. case PARTITION_CUSTOM:
  9. case PARTITION_RANGE:
  10. case PARTITION_FORCED_REBALANCE:
  11. distributionPattern = DistributionPattern.ALL_TO_ALL;
  12. break;
  13. default:
  14. throw new RuntimeException("Unknown runtime ship strategy: " + channel.getShipStrategy());
  15. }

ExecutionVertex之间如何链接:

  • ALL_TO_ALL模式:

    则每一个并行的ExecutionVertex节点都会链接到源节点产生的所有中间结果IntermediateResultPartition

  • POINTWISE模式:

    • 源的并行度和目标并行度相等。这种情况下,采用一对一的链接方式:

    • 源的并行度小于目标并行度。这种情况下,对于每一个执行节点链接到的源的中间结果分区由如下公式计算得到:

      1. sourcePartition = (int)subTaskIndex / (((float) parallelism) / numSources)

    1. ![](https://img2018.cnblogs.com/blog/413838/201810/413838-20181007200503601-2116591947.png)
    2. * 源的并行度大于目标并行度。这种情况下,计算每一个执行节点会平均链接到几个源节点,平均分配后余下的都分给最后一个节点。
    3. ![](https://img2018.cnblogs.com/blog/413838/201810/413838-20181007200644222-1739689498.png)

最后提交给TaskManagerTaskDeploymentDescriptor如下:

ResultPartitionDeploymentDescriptor有一个numberOfSubpartitions字段,其等于此ResultPartition的消费者的数量(被下级链接到的边数),因为最终执行的时候每一个ResultPartition还会拆分为numberOfSubpartitions相同数量的ResultSubPartition

InputGateDeploymentDescriptor包含多个InputChannelDeploymentDescriptor和一个用以指示消费第几个ResultSubPartitionconsumedSubpartitionIndex。每一个InputGateDeploymentDescriptor消费的所有ResultPartitionsubPartitionIndex是一样的。

例如并行度均为2的两个ExecutionJobVertex采用ALL_TO_ALL方式链接的结果如下:

TaskManager

TaskManager接收到TaskDeploymentDescriptor对象后进行反序列化生成Task对象并进行一系列的初始化操作(如:根据ResultPartitionDeploymentDescriptor对象初始化writers[ResultPartitionWriter],根据InputGateDeploymentDescriptor初始化inputGates[SingleInputGate],重新设置classLoader等)然后启用新线程执行invokable[AbstractInvokable]->invoke方法。

也就是说Task的主要业务逻辑其实都包含在了AbstractInvokable对象中,我们来具体看下其子类StreamTask(SourceStreamTaskOneInputStreamTask)

StreamTaskinvoke方法会创建OperatorChain

重点关注chainEntryPoint这个属性是BroadcastingOutputCollector类型,其collect方法如下:

  1. public void collect(StreamRecord<T> record) {
  2. for (Output<StreamRecord<T>> output : outputs) {
  3. output.collect(record);
  4. }
  5. }

即使依次遍历链中的每一个output进行collect操作,而其中的每一个output又是ChainingOutput及其子类。

  1. @Override
  2. public void collect(StreamRecord<T> record) {
  3. if (this.outputTag != null) {
  4. // we are only responsible for emitting to the main input
  5. return;
  6. }
  7. pushToOperator(record);
  8. }
  9. protected <X> void pushToOperator(StreamRecord<X> record) {
  10. try {
  11. // we know that the given outputTag matches our OutputTag so the record
  12. // must be of the type that our operator expects.
  13. @SuppressWarnings("unchecked")
  14. StreamRecord<T> castRecord = (StreamRecord<T>) record;
  15. numRecordsIn.inc();
  16. operator.setKeyContextElement1(castRecord);
  17. operator.processElement(castRecord);
  18. }
  19. catch (Exception e) {
  20. throw new ExceptionInChainedOperatorException(e);
  21. }
  22. }

其中operatorOneInputStreamOperator类型其子类业务实现逻辑(processElement)方法:调用用户自定义函数userFunction[Function]处理后按需调用output.collect(element)其中output可能也是一个ChainingOutput类型,这样整个执行链路就被一级一级链接起来了。

  1. this.chainEntryPoint = createOutputCollector(
  2. containingTask,
  3. configuration,
  4. chainedConfigs,
  5. userCodeClassloader,
  6. streamOutputMap,
  7. allOps);
  8. if (headOperator != null) {
  9. Output output = getChainEntryPoint();
  10. headOperator.setup(containingTask, configuration, output);
  11. }

对于StreamTask常见的一个子类SourceStreamTask,其run方法:

  1. @Override
  2. protected void run() throws Exception {
  3. headOperator.run(getCheckpointLock(), getStreamStatusMaintainer());
  4. }

对于OperatorChain链上最后一个operatoroutputRecordWriterOutput类型其封装了StreamRecordWriter配合ChannelSelector写入到具体的某个ResultSubPartition


  1. public void emit(T record) throws IOException, InterruptedException {
  2. for (int targetChannel : channelSelector.selectChannels(record, numChannels)) {
  3. sendToTarget(record, targetChannel);
  4. }
  5. }

常见的ChannelSelector:

  • RescalePartitioner|RebalancePartitioner: 轮询
  • KeyGroupStreamPartitioner: 基于key分组
  • GlobalPartitioner: 全局,只通过subpartition==0
  • ShufflePartitioner:随机到子分区
  • ForwardPartitioner: 本地转发
  • BroadcastPartitioner: 广播到所有分区

另一个StreamTask常见的一个子类OneInputStreamTask,其run方法:

  1. @Override
  2. protected void run() throws Exception {
  3. // cache processor reference on the stack, to make the code more JIT friendly
  4. final StreamInputProcessor<IN> inputProcessor = this.inputProcessor;
  5. while (running && inputProcessor.processInput()) {
  6. // all the work happens in the "processInput" method
  7. }
  8. }

inputProcessorStreamInputProcessor类型,在init方法中创建


  1. if (checkpointMode == CheckpointingMode.EXACTLY_ONCE) {
  2. long maxAlign = taskManagerConfig.getLong(TaskManagerOptions.TASK_CHECKPOINT_ALIGNMENT_BYTES_LIMIT);
  3. if (!(maxAlign == -1 || maxAlign > 0)) {
  4. throw new IllegalConfigurationException(
  5. TaskManagerOptions.TASK_CHECKPOINT_ALIGNMENT_BYTES_LIMIT.key()
  6. + " must be positive or -1 (infinite)");
  7. }
  8. this.barrierHandler = new BarrierBuffer(inputGate, ioManager, maxAlign);
  9. }
  10. else if (checkpointMode == CheckpointingMode.AT_LEAST_ONCE) {
  11. this.barrierHandler = new BarrierTracker(inputGate);
  12. }

barrierHandler与设置的CheckpointingMode相关:

  • EXACTLY_ONCE:BarrierBuffer
  • AT_LEAST_ONCE:BarrierTracker

inputProcessorprocessInput方法会调用barrierHandler.getNextNonBlocked()如果获取到一条完整记录则调用streamOperator.processElement(record)触发整体调用链的执行。

Flink standalone模式作业执行流程的更多相关文章

  1. 追源索骥:透过源码看懂Flink核心框架的执行流程

    li,ol.inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt, ...

  2. 透过源码看懂Flink核心框架的执行流程

    前言 Flink是大数据处理领域最近很火的一个开源的分布式.高性能的流式处理框架,其对数据的处理可以达到毫秒级别.本文以一个来自官网的WordCount例子为引,全面阐述flink的核心架构及执行流程 ...

  3. Spark架构与作业执行流程简介(scala版)

    在讲spark之前,不得不详细介绍一下RDD(Resilient Distributed Dataset),打开RDD的源码,一开始的介绍如此: 字面意思就是弹性分布式数据集,是spark中最基本的数 ...

  4. Spark架构与作业执行流程简介

    https://www.cnblogs.com/shenh062326/p/3658543.html

  5. Flink架构分析之Standalone模式启动流程

    概述 FLIP6 对Flink架构进行了改进,引入了Dispatcher组件集成了所有任务共享的一些组件:SubmittedJobGraphStore,LibraryCacheManager等,为了保 ...

  6. Map/Reduce 工作机制分析 --- 作业的执行流程

    前言 从运行我们的 Map/Reduce 程序,到结果的提交,Hadoop 平台其实做了很多事情. 那么 Hadoop 平台到底做了什么事情,让 Map/Reduce 程序可以如此 "轻易& ...

  7. 第九篇:Map/Reduce 工作机制分析 - 作业的执行流程

    前言 从运行我们的 Map/Reduce 程序,到结果的提交,Hadoop 平台其实做了很多事情. 那么 Hadoop 平台到底做了什么事情,让 Map/Reduce 程序可以如此 "轻易& ...

  8. 面试高频SpringMVC执行流程最优解(源码分析)

    文章已托管到GitHub,大家可以去GitHub查看阅读,欢迎老板们前来Star! 搜索关注微信公众号 码出Offer 领取各种学习资料! SpringMVC执行流程 SpringMVC概述 Spri ...

  9. Thinkphp设计模式和执行流程

    ThinkPHP设计模式 单例模式:数据库连接DB工厂模式:比如Db.class.php中的factory()方法适配器模式:驱动类,数据库观察者模式:Hook类 注册树模式:绑定容器外观模式:fac ...

随机推荐

  1. 五大问题,详解阿里云PTS铂金版

    阿里云PTS铂金版,具备强大的分布式压测能力,相比业界产品的云主机发起,该产品更快速,来源更广泛,脉冲能力和流量掌控能力更强.日前,阿里云推出了PTS铂金版尝鲜包,旨在为用户提供高性价比的最佳实践.我 ...

  2. JavaScript原型链基础(prototype chain)

    1.函数基础 2.对象基础 3.原型链基础

  3. 如何修改word文档中每行字符的最大默认值和每页最大行数默认值

    事情起因是这样的,小明在写论文的过程中,发现自己的文档的字与字的间距看起来比其他人的字符间距大,于是觉得奇怪,明明设置了一样的格式啊,设置每行38个字符,每页34行,为什么小明写的文档字符间距看着比较 ...

  4. docker 1.13.1 启动容器过程中mount报错

    docker 1.13.1 启动container 问题 [root@openfalcon_transfer1 harbor]# docker run -it --name test busybox ...

  5. 写出js内存泄漏的问题?

    回答一: (1)IE7/8 DOM对象或者Active对象循环引用导致内存泄漏 a.多个对象循环引用 b.循环的DOM泄漏 (2)基础的DOM泄漏 当原有的DOM被移除时,子节点引用没有被移除则无法回 ...

  6. uniform_tree以及其变体

    //判断一棵树是不是uniform-tree bool uniform_tree(TreeNode* root){ if(root == NULL) return true; return unifo ...

  7. Node.js实战(四)之调试Node.js

    当项目逐渐扩大以后,功能越来越多,这时有的时候需要增加或者修改,同时优化某些功能,就有可能出问题了.针对于线上Linux环境我们应该如何调试项目呢? 别怕,Node.js已经为我们考虑到了. 通过 n ...

  8. weblogic92一次成功修改密码的记录

    假设你忘记了weblogic92控制台的密码了: 假设你的hostname叫localhost.localdomain 假设你的bea在/opt下: ------------------------- ...

  9. Pyspider抓取静态页面

    近期,我想爬一批新闻资讯的内容.新闻类型的网址很多,我想看看有没有一个网页上能包罗尽可能多的新闻网站呢,于是就发现了下面这个网页 http://news.hao123.com/wangzhi 这个页面 ...

  10. switchable图片切换

    前提: 最近由于项目的需要jquery "switchable图片切换"效果 所以趁着周末有空时间研究下 ,以前工作都依赖于kissy框架,所以也没有综合的写过类似的,如下图所示效 ...