结合Flink1.8源码,分析数据在Flink各Task(算子)之间的流转过程,有利于理解Flink运行逻辑,方便定位问题。
内容如下:
Function –> StreamOperator –> StreamTransformation –> DataStream
根据用户StreamApi的逻辑构建执行计划,有上述四个核心接口,其中StreamOperator是Flink内部各算子处理数据的关键部分。
参考:https://blog.csdn.net/super_wj0820/article/details/81141650
TaskManager \ TaskExecutor是Flink真正工作的work,上面运行各个Task,以OnYarn模式为例,Dispatcher启动JobMaster服务后,会生成ExecutionGraph,通过Execution来deploy各个Task(submitTask的过程)
参考:https://blog.csdn.net/super_wj0820/article/details/90726768
ExecutionGraph的生成
- ExecutionGraph由JobGraph转化而成,是JobGraph的并行表示;
- ExecutionGraph是一个DAG图,由ExecutionJobVertex、IntermediateResult(或IntermediateResultPartition)组成;
- ExecutionJobVertex由多个并行的ExecutionVertex所组成
- 一个ExecutionVertex可能对应多个运行状态的Execution,比如,一个ExecutionVertex运行产生了一个失败的Execution,然后还会创建一个新的Execution来运行,这时就对应这个2次运行Attempt。
- Execution的deploy方法中,通过taskManagerGateway.submitTask提交Task
- 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处理数据
调用顺序:
任务提交后,Task中的run()方法是所有subtask的核心入口;
加载AbstractInvokable的实现;
invokable.invoke()方法调用用户的具体业务逻辑;
StreamTask中调用各子类的run()方法;
通过StreamInputProcessor.processInput()方法处理每条数据;(while循环中)
StreamInputProcessor.processInput()方法中,会用到StreamOperator.processElement方法处理元素
注意: 此处的StreamOperator对象就是预备知识点1中的核心接口,此处就和用户代码联系起来了!
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子类实现:
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()方法。
Stream任务输入数据处理器(分别处理算子上游一个/二个输入的情况)
processInput()方法中,会调用用户定义的StreamOperator.processElement(record)处理数据
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;
}
}
}