Flink数据流转——Task之间(用户API层面)

结合Flink1.8源码,分析数据在Flink各Task(算子)之间的流转过程,有利于理解Flink运行逻辑,方便定位问题。

内容如下:

  1. 预备知识介绍
  2. 方法调用栈与关键类介绍

预备知识点

  1. Function –> StreamOperator –> StreamTransformation –> DataStream
    根据用户StreamApi的逻辑构建执行计划,有上述四个核心接口,其中StreamOperator是Flink内部各算子处理数据的关键部分。
    参考:https://blog.csdn.net/super_wj0820/article/details/81141650

  2. TaskManager \ TaskExecutor是Flink真正工作的work,上面运行各个Task,以OnYarn模式为例,Dispatcher启动JobMaster服务后,会生成ExecutionGraph,通过Execution来deploy各个Task(submitTask的过程)
    参考:https://blog.csdn.net/super_wj0820/article/details/90726768

  3. ExecutionGraph的生成

    1. ExecutionGraph由JobGraph转化而成,是JobGraph的并行表示;
    2. ExecutionGraph是一个DAG图,由ExecutionJobVertex、IntermediateResult(或IntermediateResultPartition)组成;
    3. ExecutionJobVertex由多个并行的ExecutionVertex所组成
    4. 一个ExecutionVertex可能对应多个运行状态的Execution,比如,一个ExecutionVertex运行产生了一个失败的Execution,然后还会创建一个新的Execution来运行,这时就对应这个2次运行Attempt。
    5. Execution的deploy方法中,通过taskManagerGateway.submitTask提交Task
    6. taskManagerGateway.submitTask的不同实现类对应了不同部署方式

    参考1:https://blog.csdn.net/super_wj0820/article/details/81142460
    参考2:https://blog.csdn.net/super_wj0820/article/details/81142710

方法调用栈与关键类介绍

引言

Execution的deploy方法中,通过taskManagerGateway.submitTask方法提交Task,根据不同的部署方式,TaskManagerGateway对应不同的实现:

ActorTaskManagerGateway (org.apache.flink.runtime.jobmanager.slots)
	-- Local、Standalone模式
RpcTaskManagerGateway (org.apache.flink.runtime.jobmaster)
	-- OnYarn模式

以OnYarn模式为例,RpcTaskManagerGateway网关是通过TaskExecutorGateway为TaskExecutor(等同于TaskManager)部署、启动Task,方法栈如下:

// 遍历 execution,调用其 deploy 方法
execution.deploy()
	--> taskManagerGateway.submitTask
	--> [TaskExecutor] new Task()
	--> [TaskExecutor] task.startTaskThread()	-- 至此,任务真正执行

Task封装了TaskExecutor(即TaskManager)上一个并行子任务(subtask)的运行逻辑,Task对象实现了Runnable接口,run()方法是各subtask的核心入口。

关键方法栈

Task
	-> run();
	-> invokable = loadAndInstantiateInvokable			-- 加载此Task的AbstractInvokable实现类对象
	-> invokable.invoke();								-- AbstractInvokable的核心方法,流式计算实现类是 StreamTask, invoke方法中会调用子类的run()方法
		-> OneInputStreamTask.run()
		-> inputProcessor.processInput()				-- StreamInputProcessor的processInput()方法完成用户输入数据的处理(用户数据、watermark、checkpoint数据)
		-> streamOperator.processElement(record)		-- StreamOperator处理数据

调用顺序:

  1. 任务提交后,Task中的run()方法是所有subtask的核心入口;

  2. 加载AbstractInvokable的实现;

  3. invokable.invoke()方法调用用户的具体业务逻辑;

  4. StreamTask中调用各子类的run()方法;

  5. 通过StreamInputProcessor.processInput()方法处理每条数据;(while循环中)

  6. StreamInputProcessor.processInput()方法中,会用到StreamOperator.processElement方法处理元素

    注意: 此处的StreamOperator对象就是预备知识点1中的核心接口,此处就和用户代码联系起来了!
    

核心类与方法

Task

  1. TaskManager上执行的并行子任务(subtask)
  2. Task包装Flink Operator(可以是用户函数)并运行它,提供所有必需的服务
  3. run()方法中,TaskManager调用用AbstractInvokable.invoke()执行一个Task
Task.java

@Override
public void run() {

	// ...

	try {		

		// 初始化 the task's invokable code
		invokable = loadAndInstantiateInvokable(userCodeClassLoader, nameOfInvokableClass, env);

		// ----------------------------------------------------------------
		//  以下是Task的核心部分
		// ----------------------------------------------------------------

		this.invokable = invokable;

		// 调用 invokable 方法,方法中有循环处理
		invokable.invoke();

		// ...
	}
	catch (Throwable t) {
		// ...
	}
	finally {
		// ...
	}
}

AbstractInvokable

  1. AbstractInvokable是TaskManager可以执行的每个Task的抽象基类
  2. TaskManager通过invoke()方法执行Task,Task的所有逻辑都在此方法中
  3. 对应stream/batch任务,对应不同实现类:StreamTask/BatchTask

AbstractInvokable子类实现:

AbstractInvokable (org.apache.flink.runtime.jobgraph.tasks)
    StreamTask (org.apache.flink.streaming.runtime.tasks)
        SourceStreamTask (org.apache.flink.streaming.runtime.tasks)
        TwoInputStreamTask (org.apache.flink.streaming.runtime.tasks)
        OneInputStreamTask (org.apache.flink.streaming.runtime.tasks)
    BatchTask (org.apache.flink.runtime.operators)

StreamTask 中定义了AbstractInvokable实现类的完整生命周期方法:

	// 初始化操作
	protected abstract void init() throws Exception;
	// 数据处理的核心方法
	protected abstract void run() throws Exception;
	// 清理操作
	protected abstract void cleanup() throws Exception;
	// 取消Task
	protected abstract void cancelTask() throws Exception;

StreamTask在invoke()方法调用过程中,会调用上述 run() 抽象方法的子类实现:

StreamTask.java

@Override
public final void invoke() throws Exception {

	boolean disposed = false;
	try {
		// -------- Initialize ---------
		init();

		// let the task do its work
		isRunning = true;
		run();

		// ...
	}
	finally {
		// ...
	}
}

以StreamTask的一个实现类:OneInputStreamTask(处理一个输入的情况)为例,看下run()方法的实现:

OneInputStreamTask.java

@Override
protected void run() throws Exception {
	// cache processor reference on the stack, to make the code more JIT friendly
	final StreamInputProcessor inputProcessor = this.inputProcessor;

	// 在循环中不断处理输入数据
	while (running && inputProcessor.processInput()) {
		// all the work happens in the "processInput" method
	}
}

后续继续分析inputProcessor.processInput()方法。

StreamInputProcessor/StreamTwoInputProcessor

  1. Stream任务输入数据处理器(分别处理算子上游一个/二个输入的情况)

  2. processInput()方法中,会调用用户定义的StreamOperator.processElement(record)处理数据

  3. processInput()方法中,StatusWatermarkValve会处理Watermark、StreamStatus和LatencyMarker

     补充:
     1. Watermark是事件时间的水位线
     2. StreamStatus表示此subtask是否从input继续接收record或Watermark
     3. LatencyMarker是衡量数据的延迟时间(从source到sink的时间)
    
public boolean processInput() throws Exception {
	// ...

	while (true) {
		if (currentRecordDeserializer != null) {
			DeserializationResult result = currentRecordDeserializer.getNextRecord(deserializationDelegate);

			if (result.isBufferConsumed()) {
				currentRecordDeserializer.getCurrentBuffer().recycleBuffer();
				currentRecordDeserializer = null;
			}

			if (result.isFullRecord()) {
				StreamElement recordOrMark = deserializationDelegate.getInstance();

				if (recordOrMark.isWatermark()) {
					// 处理 watermark
					statusWatermarkValve.inputWatermark(recordOrMark.asWatermark(), currentChannel);
					continue;
				} else if (recordOrMark.isStreamStatus()) {
					// 处理 stream status,表示是否从input继续接收record或Watermark
					statusWatermarkValve.inputStreamStatus(recordOrMark.asStreamStatus(), currentChannel);
					continue;
				} else if (recordOrMark.isLatencyMarker()) {
					// 处理 latency marker
					synchronized (lock) {
						streamOperator.processLatencyMarker(recordOrMark.asLatencyMarker());
					}
					continue;
				} else {
					// 真正处理数据的地方
					StreamRecord record = recordOrMark.asRecord();
					synchronized (lock) {
						numRecordsIn.inc();
						streamOperator.setKeyContextElement1(record);
						// StreamOperator处理数据
						streamOperator.processElement(record);
					}
					return true;
				}
			}
		}

		final BufferOrEvent bufferOrEvent = barrierHandler.getNextNonBlocked();
		if (bufferOrEvent != null) {
			if (bufferOrEvent.isBuffer()) {
				currentChannel = bufferOrEvent.getChannelIndex();
				currentRecordDeserializer = recordDeserializers[currentChannel];
				currentRecordDeserializer.setNextBuffer(bufferOrEvent.getBuffer());
			}
			else {
				// Event received
				final AbstractEvent event = bufferOrEvent.getEvent();
				if (event.getClass() != EndOfPartitionEvent.class) {
					throw new IOException("Unexpected event: " + event);
				}
			}
		}
		else {
			isFinished = true;
			if (!barrierHandler.isEmpty()) {
				throw new IllegalStateException("Trailing data in checkpoint barrier handler.");
			}
			return false;
		}
	}
}

你可能感兴趣的:(Flink)