El programador es el componente central de la ejecución de trabajos de Flink. Gestiona todos los procesos relacionados con la ejecución de trabajos, incluida
la conversión de JobGraph a ExecutionGraph, la gestión del ciclo de vida del trabajo (liberación, cancelación y detención del trabajo) y la
gestión del ciclo de vida de la tarea del trabajo. (Liberación de tareas, Cancelación, detención), aplicación y liberación de recursos, conmutación por error de tareas y trabajos, etc.
La programación tiene varios componentes importantes:
⚫ Programador: SchedulerNG y sus subclases,
clases de implementación Estrategia de programación: SchedulingStrategy y sus
clases de implementación Modo de programación: ScheduleMode incluye programación de flujo y por lotes, con diferentes modos de programación
La función del programador:
1) Gestión del ciclo de vida del trabajo, como liberación, suspensión y cancelación del trabajo
2) Aplicación, asignación y liberación de recursos de ejecución del trabajo
3) Gestión del estado del trabajo, cambios de estado durante el lanzamiento del trabajo y excepciones del trabajo FailOver, etc. .
4) proporcionar información del trabajo, proporcionan información detallada del trabajo con el mundo exterior
public interface SchedulerNG {
void setMainThreadExecutor(ComponentMainThreadExecutor mainThreadExecutor);
void registerJobStatusListener(JobStatusListener jobStatusListener);
void startScheduling();
void suspend(Throwable cause);
void cancel();
CompletableFuture<Void> getTerminationFuture();
void handleGlobalFailure(Throwable cause);
default boolean updateTaskExecutionState(TaskExecutionState taskExecutionState) {
return updateTaskExecutionState(new TaskExecutionStateTransition(taskExecutionState));
}
boolean updateTaskExecutionState(TaskExecutionStateTransition taskExecutionState);
SerializedInputSplit requestNextInputSplit(JobVertexID vertexID, ExecutionAttemptID
executionAttempt) throws IOException;
ExecutionState requestPartitionState(IntermediateDataSetID intermediateResultId, ResultPartitionID
resultPartitionId) throws PartitionProducerDisposedException;
void scheduleOrUpdateConsumers(ResultPartitionID partitionID);
ArchivedExecutionGraph requestJob();
JobStatus requestJobStatus();
JobDetails requestJobDetails();
}
Clase de implementación: DefaultScheduler (1.11 eliminado LegacyScheduler)
Comportamiento de programación
/**
* Component which encapsulates the scheduling logic.
* It can react to execution state changes and partition consumable events.
* Moreover, it is responsible for resolving task failures.
*/
public interface SchedulingStrategy {
/**
* Called when the scheduling is started (initial scheduling operation).
* 调度入口,触发调度器的调度行为
*/
void startScheduling();
/**
* Called whenever vertices need to be restarted (due to task failure).
* 重启执行失败的 Task,一般是 Task 执行异常导致
* @param verticesToRestart The tasks need to be restarted
*/
void restartTasks(Set<ExecutionVertexID> verticesToRestart);
/**
* Called whenever an {@link Execution} changes its state.
* 当 Execution 改变状态时调用
* @param executionVertexId The id of the task
* @param executionState The new state of the execution
*/
void onExecutionStateChange(ExecutionVertexID executionVertexId, ExecutionState executionState);
/**
* Called whenever an {@link IntermediateResultPartition} becomes consumable.
* 当 IntermediateResultPartition 中的数据可以消费时调用
* @param resultPartitionId The id of the result partition
*/
void onPartitionConsumable(IntermediateResultPartitionID resultPartitionId);
}
Modo de programación
ScheduleMode determina cómo iniciar Task en ExecutionGraph. Flink proporciona 3 modos de programación:
1) La programación ansiosa es
adecuada para la computación de flujo. Solicite todos los recursos necesarios a la vez. Si los recursos son insuficientes, el trabajo no se iniciará.
2) La programación por etapas
LAZY_FROM_SOURCES es adecuada para el procesamiento por lotes. Comience a programar en etapas desde SourceTask. Cuando solicite recursos
, solicite todos los recursos necesarios en esta etapa a la vez. Una vez ejecutada la tarea ascendente, se programa la ejecución de la tarea descendente,
se leen los datos ascendentes y se ejecuta la tarea de cálculo de esta etapa. Una vez completada la ejecución, se programan las tareas de la siguiente etapa
y se programan por turnos hasta que se complete el trabajo.
3) Programación de reutilización de
ranuras en fase LAZY_FROM_SOURCES_WITH_BATCH_SLOT_REQUEST es adecuada para el procesamiento por lotes.
Básicamente es lo mismo que la programación por fases , la diferencia es que el modo de aplicación de recursos por lotes se usa en este modo y
el trabajo se puede ejecutar cuando los recursos son insuficientes , pero es necesario asegurarse de que no haya un comportamiento Shuffle en el ejecución del trabajo en esta etapa.
El modo Eager actual a la vista tiene la misma lógica de aplicación de recursos que el modo
LAZY_FROM_SOURCES . LAZY_FROM_SOURCES_WITH_BATCH_SLOT_REQUEST es una lógica de aplicación de recursos separada.
public enum ScheduleMode {
LAZY_FROM_SOURCES(true),
LAZY_FROM_SOURCES_WITH_BATCH_SLOT_REQUEST(true),
EAGER(false);
private final boolean allowLazyDeployment;
ScheduleMode(boolean allowLazyDeployment) {
this.allowLazyDeployment = allowLazyDeployment;
}
public boolean allowLazyDeployment() {
return allowLazyDeployment;
}
}
Estrategia de programación
Hay tres implementaciones de estrategias de programación:
⚫ EagerSchedulingStrategy: adecuada para la computación en flujo, programando todas las tareas al mismo tiempo
⚫ LazyFromSourcesSchedulingStrategy: adecuada para el procesamiento por lotes, cuando los datos de entrada están listos (el procesamiento en sentido ascendente ha
finalizado), se realiza la programación de vértices.
⚫ PipelinedRegionSchedulingStrategy: programación con la granularidad de la
parte de canalización. La estrategia PipelinedRegionSchedulingStrategy se agregó en 1.11. A partir de 1.12, la
programación se realizará en unidades de regiones de canalización . Una región canalizada es un conjunto de tareas conectadas por una canalización. Esto significa que para
un trabajo de transmisión que contiene varias regiones , ya no espera a que todas las tareas adquieran ranuras antes de comenzar a implementar tareas. En cambio, una vez que una
región ha obtenido suficientes espacios para tareas, se puede implementar. Para los trabajos por lotes, no se asignará ningún espacio a la tarea
y la tarea no se implementará por separado. En cambio, una vez que una región tenga suficientes espacios, la tarea se
implementará en la misma región junto con todas las demás tareas.