programador de delfines (三)

Prefacio

El artículo anterior hablaba apresuradamente de dos clases, esta vez continuaremos con la clase MasterExecThread, y continuaremos.

一 、 MasterExecThread

El artículo anterior decía que el método de envío de tareas es submitStandByTask (). El siguiente código es el siguiente. Colocará todas las tareas que pueden cumplir con las condiciones de envío después del análisis anterior en el objeto readyToSubmitTaskList, y luego una por una desde este Saque la tarea y pásala al método submitTaskExec (tarea).

 /**
     * handling the list of tasks to be submitted
     */
    private void submitStandByTask(){
    
    
        for(Map.Entry<String, TaskInstance> entry: readyToSubmitTaskList.entrySet()) {
    
    
            TaskInstance task = entry.getValue();
            DependResult dependResult = getDependResultForTask(task);
            if(DependResult.SUCCESS == dependResult){
    
    
                if(retryTaskIntervalOverTime(task)){
    
    
                    submitTaskExec(task);
                    removeTaskFromStandbyList(task);
                }
            }else if(DependResult.FAILED == dependResult){
    
    
                // if the dependency fails, the current node is not submitted and the state changes to failure.
                dependFailedTask.put(entry.getKey(), task);
                removeTaskFromStandbyList(task);
                logger.info("task {},id:{} depend result : {}",task.getName(), task.getId(), dependResult);
            }
        }
    }
    

Comience a determinar el tipo de tarea e inyecte el tipo de tarea en el constructor de la subclase de MasterBaseTaskExecThread.El MasterBaseTaskExecThread implementa la interfaz Callable.
Luego, pase la subclase del MasterBaseTaskExecThread instanciado al grupo de subprocesos para ayudar a ejecutarlo y guarde la referencia de la instancia en el mapa de activeTaskNode.

/**
     * submit task to execute
     * @param taskInstance task instance
     * @return TaskInstance
     */
    private TaskInstance submitTaskExec(TaskInstance taskInstance) {
    
    
        MasterBaseTaskExecThread abstractExecThread = null;
        if(taskInstance.isSubProcess()){
    
    
            abstractExecThread = new SubProcessTaskExecThread(taskInstance);
        }else if(taskInstance.isDependTask()){
    
    
            abstractExecThread = new DependentTaskExecThread(taskInstance);
        }else if(taskInstance.isConditionsTask()){
    
    
            abstractExecThread = new ConditionsTaskExecThread(taskInstance);
        }else {
    
    
            abstractExecThread = new MasterTaskExecThread(taskInstance);
        }
        Future<Boolean> future = taskExecService.submit(abstractExecThread);
        activeTaskNode.putIfAbsent(abstractExecThread, future);
        return abstractExecThread.getTaskInstance();
    }

Echemos un vistazo a para qué sirven los nodos guardados en activeTaskNode. Puede ver dónde se llaman de la siguiente manera. Se usa para monitorear si el nodo de tarea actual se ha procesado para facilitar el procesamiento del siguiente paso, o si el usuario puede need to kill Para esta tarea, se requiere el índice de este nodo.
Inserte la descripción de la imagen aquí

二 、 MasterBaseTaskExecThread

Luego, observe el contenido del método de llamada, de la siguiente manera.

/**
     * call
     * @return boolean
     * @throws Exception exception
     */
    @Override
    public Boolean call() throws Exception {
    
    
        this.processInstance = processService.findProcessInstanceById(taskInstance.getProcessInstanceId());
        return submitWaitComplete();
    }

Su subclase reescribe el método submitWaitComplete (), elige un MasterTaskExecThread para ver los resultados de su envío, compuesto principalmente por el comportamiento de envío y el comportamiento de esperar a que la tarea se envíe correctamente y luego salir.

/**
     * submit task instance and wait complete
     *
     * @return true is task quit is true
     */
    @Override
    public Boolean submitWaitComplete() {
    
    
        Boolean result = false;
        this.taskInstance = submit();
        if(this.taskInstance == null){
    
    
            logger.error("submit task instance to mysql and queue failed , please check and fix it");
            return result;
        }
        if(!this.taskInstance.getState().typeIsFinished()) {
    
    
            result = waitTaskQuit();
        }
        taskInstance.setEndTime(new Date());
        processService.updateTaskInstance(taskInstance);
        logger.info("task :{} id:{}, process id:{}, exec thread completed ",
                this.taskInstance.getName(),taskInstance.getId(), processInstance.getId() );
        return result;
    }

//然后又看一下其父类提交的方法
 * submit master base task exec thread
     * @return TaskInstance
     */
    protected TaskInstance submit() {
    
    
        Integer commitRetryTimes = masterConfig.getMasterTaskCommitRetryTimes();
        Integer commitRetryInterval = masterConfig.getMasterTaskCommitInterval();

        int retryTimes = 1;
        boolean submitDB = false;
        boolean submitTask = false;
        TaskInstance task = null;
        while (retryTimes <= commitRetryTimes){
    
    
            try {
    
    
                if(!submitDB){
    
    
                    // submit task to db
                    task = processService.submitTask(taskInstance);
                    if(task != null && task.getId() != 0){
    
    
                        submitDB = true;
                    }
                }
                if(submitDB && !submitTask){
    
    
                    // dispatch task
                    submitTask = dispatchTask(task);
                }
                if(submitDB && submitTask){
    
    
                    return task;
                }
                if(!submitDB){
    
    
                    logger.error("task commit to db failed , taskId {} has already retry {} times, please check the database", taskInstance.getId(), retryTimes);
                }else if(!submitTask){
    
    
                    logger.error("task commit  failed , taskId {} has already retry {} times, please check", taskInstance.getId(), retryTimes);
                }
                Thread.sleep(commitRetryInterval);
            } catch (Exception e) {
    
    
                logger.error("task commit to mysql and dispatcht task failed",e);
            }
            retryTimes += 1;
        }
        return task;
    }
任务信息成功保存到数据库后,看一下它怎么将任务分发出去的,就是如下的方法啦,不难看到将此任务的重要的部分任务信息塞进队列里面去,另一端又从队列里面消费此任务。
    /**
     * dispatcht task
     * @param taskInstance taskInstance
     * @return whether submit task success
     */
    public Boolean dispatchTask(TaskInstance taskInstance) {
    
    

        try{
    
    
            if(taskInstance.isConditionsTask()
                    || taskInstance.isDependTask()
                    || taskInstance.isSubProcess()){
    
    
                return true;
            }
            if(taskInstance.getState().typeIsFinished()){
    
    
                logger.info(String.format("submit task , but task [%s] state [%s] is already  finished. ", taskInstance.getName(), taskInstance.getState().toString()));
                return true;
            }
            // task cannot be submitted because its execution state is RUNNING or DELAY.
            if (taskInstance.getState() == ExecutionStatus.RUNNING_EXECUTION
                    || taskInstance.getState() == ExecutionStatus.DELAY_EXECUTION) {
    
    
                logger.info("submit task, but the status of the task {} is already running or delayed.", taskInstance.getName());
                return true;
            }
            logger.info("task ready to submit: {}", taskInstance);

            /**
             *  taskPriorityInfo
             */
            String taskPriorityInfo = buildTaskPriorityInfo(processInstance.getProcessInstancePriority().getCode(),
                    processInstance.getId(),
                    taskInstance.getProcessInstancePriority().getCode(),
                    taskInstance.getId(),
                    org.apache.dolphinscheduler.common.Constants.DEFAULT_WORKER_GROUP);
            taskUpdateQueue.put(taskPriorityInfo);
            logger.info(String.format("master submit success, task : %s", taskInstance.getName()) );
            return true;
        }catch (Exception e){
    
    
            logger.error("submit task  Exception: ", e);
            logger.error("task error : %s", JSONUtils.toJsonString(taskInstance));
            return false;
        }
    }

Entonces eche un vistazo a este método de esperar a que la tarea salga No es difícil ver que el siguiente es un bucle infinito, lo que indica que los recursos de subprocesos ocupados por la tarea se recuperarán solo después de que se complete la tarea.

/**
     * polling db
     *
     * wait task quit
     * @return true if task quit success
     */
    public Boolean waitTaskQuit(){
    
    
        // query new state
        taskInstance = processService.findTaskInstanceById(taskInstance.getId());
        logger.info("wait task: process id: {}, task id:{}, task name:{} complete",
                this.taskInstance.getProcessInstanceId(), this.taskInstance.getId(), this.taskInstance.getName());

        while (Stopper.isRunning()){
    
    
            try {
    
    
                if(this.processInstance == null){
    
    
                    logger.error("process instance not exists , master task exec thread exit");
                    return true;
                }
                // task instance add queue , waiting worker to kill
                if(this.cancel || this.processInstance.getState() == ExecutionStatus.READY_STOP){
    
    
                    cancelTaskInstance();
                }
                if(processInstance.getState() == ExecutionStatus.READY_PAUSE){
    
    
                    pauseTask();
                }
                // task instance finished
                if (taskInstance.getState().typeIsFinished()){
    
    
                    // if task is final result , then remove taskInstance from cache
                    taskInstanceCacheManager.removeByTaskInstanceId(taskInstance.getId());
                    break;
                }
                if (checkTaskTimeout()) {
    
    
                    this.checkTimeoutFlag = !alertTimeout();
                }
                // updateProcessInstance task instance
                taskInstance = processService.findTaskInstanceById(taskInstance.getId());
                processInstance = processService.findProcessInstanceById(processInstance.getId());
                Thread.sleep(Constants.SLEEP_TIME_MILLIS);
            } catch (Exception e) {
    
    
                logger.error("exception",e);
                if (processInstance != null) {
    
    
                    logger.error("wait task quit failed, instance id:{}, task id:{}",
                            processInstance.getId(), taskInstance.getId());
                }
            }
        }
        return true;
    }

para resumir

El lado maestro analiza la instancia del flujo de trabajo en un DAG y luego busca en el DAG para satisfacer las tareas enviadas y las guarda en el conjunto que se enviará. Luego, atraviesa esta colección para determinar el tipo de tareas enviadas y las transforma hacia abajo y las lanza al grupo de subprocesos para ayudar a enviar. Al enviar, la información de esta tarea se escribirá en la base de datos para su almacenamiento y se distribuirá a través de la cola de tareas para que la consuman otros jefes. Luego, el subproceso espera al final de la tarea para liberar los recursos del subproceso ocupados antes de regresar al grupo de subprocesos. (Deje que un hilo sirva para una tarea, en caso de que esta tarea sea una tarea que requiere mucho tiempo, se siente un poco perdida).

Supongo que te gusta

Origin blog.csdn.net/m0_48187193/article/details/114407498
Recomendado
Clasificación