programador de delfines (四)


Prefacio

He mencionado rápidamente que cuando las tareas enviadas se distribuyen, el otro extremo las coloca en la cola para su consumo. Un objeto de servicio entre los extremos maestro y trabajador también debería poder sacarse por separado para echar un vistazo. Creo que su diagrama de clases principal debería ser el siguiente ~~.
Inserte la descripción de la imagen aquí

一 、 TaskPriorityQueueImpl

Esta cola implementa los métodos en la interfaz TaskPriorityQueue y confía a la clase PriorityBlockingQueue para ayudarla a procesar la información de la tarea. Puede ver los métodos de implementación comparativos para comprender en qué reglas se basan las tareas en esta cola para priorizar las tareas.

@Service
public class TaskPriorityQueueImpl implements TaskPriorityQueue {
    
    
    /**
     * queue size
     */
    private static final Integer QUEUE_MAX_SIZE = 3000;

    /**
     * queue
     */
    private PriorityBlockingQueue<String> queue = new PriorityBlockingQueue<>(QUEUE_MAX_SIZE, new TaskInfoComparator());

    /**
     * put task takePriorityInfo
     *
     * @param taskPriorityInfo takePriorityInfo
     * @throws Exception
     */
    @Override
    public void put(String taskPriorityInfo) throws Exception {
    
    
        queue.put(taskPriorityInfo);
    }

    /**
     * take taskInfo
     * @return taskInfo
     * @throws Exception
     */
    @Override
    public String take() throws Exception {
    
    
        return queue.take();
    }

    /**
     * queue size
     * @return size
     * @throws Exception
     */
    @Override
    public int size() throws Exception {
    
    
        return queue.size();
    }

    /**
     * TaskInfoComparator
     */
    private class TaskInfoComparator implements Comparator<String>{
    
    

        /**
         * compare o1 o2
         * @param o1 o1
         * @param o2 o2
         * @return compare result
         */
        @Override
        public int compare(String o1, String o2) {
    
    
            String s1 = o1;
            String s2 = o2;
            String[] s1Array = s1.split(UNDERLINE);
            if(s1Array.length > TASK_INFO_LENGTH){
    
    
                // warning: if this length > 5, need to be changed
                s1 = s1.substring(0, s1.lastIndexOf(UNDERLINE) );
            }

            String[] s2Array = s2.split(UNDERLINE);
            if(s2Array.length > TASK_INFO_LENGTH){
    
    
                // warning: if this length > 5, need to be changed
                s2 = s2.substring(0, s2.lastIndexOf(UNDERLINE) );
            }

            return s1.compareTo(s2);
        }
    }
}

二 、 TaskPriorityQueueConsumer

Esta clase es la clase que usa la instancia de cola anterior. También es una subclase que hereda la clase de subproceso. El
método principal también está bajo el método de ejecución. Eche un vistazo. El contenido interior.

public void run() {
    
    
        List<String> failedDispatchTasks = new ArrayList<>();
        while (Stopper.isRunning()) {
    
    
            try {
    
    
                int fetchTaskNum = masterConfig.getMasterDispatchTaskNumber();
                failedDispatchTasks.clear();
                for (int i = 0; i < fetchTaskNum; i++) {
    
    
                    if (taskPriorityQueue.size() <= 0) {
    
    
                        Thread.sleep(Constants.SLEEP_TIME_MILLIS);
                        continue;
                    }
                    // if not task , blocking here
                    String taskPriorityInfo = taskPriorityQueue.take();
                    TaskPriority taskPriority = TaskPriority.of(taskPriorityInfo);
                    boolean dispatchResult = dispatch(taskPriority.getTaskId());
                    if (!dispatchResult) {
    
    
                        failedDispatchTasks.add(taskPriorityInfo);
                    }
                }
                for (String dispatchFailedTask : failedDispatchTasks) {
    
    
                    taskPriorityQueue.put(dispatchFailedTask);
                }
            } catch (Exception e) {
    
    
                logger.error("dispatcher task error", e);
            }
        }
    }

Consiste en extraer la tarea del otro extremo de la cola y luego distribuirla. Si la distribución falla, la tarea se volverá a empaquetar en la cola y luego continuará intentando distribuirla más tarde.

2. método del despachador

Observe cómo distribuye las tareas. Consulta en la base de datos los detalles de la instancia de la tarea actual según el ID de la tarea y luego crea el contexto de ejecución de la tarea según el tipo de tarea y otras condiciones. Luego se envuelve aún más en un contexto de ejecución, y este contexto de ejecución se distribuye con la ayuda del objeto ExecutorDispatcher.

 /**
     * dispatch task
     *
     * @param taskInstanceId taskInstanceId
     * @return result
     */
    private boolean dispatch(int taskInstanceId) {
    
    
        boolean result = false;
        try {
    
    
            TaskExecutionContext context = getTaskExecutionContext(taskInstanceId);
            ExecutionContext executionContext = new ExecutionContext(context.toCommand(), ExecutorType.WORKER, context.getWorkerGroup());

            if (taskInstanceIsFinalState(taskInstanceId)) {
    
    
                // when task finish, ignore this task, there is no need to dispatch anymore
                return true;
            } else {
    
    
                result = dispatcher.dispatch(executionContext);
            }
        } catch (ExecuteException e) {
    
    
            logger.error("dispatch error", e);
        }
        return result;
    }

3. El objeto ExecutorDispatcher.

Continúe persiguiendo para ver lo que está haciendo. Lo principal es seleccionar un nodo trabajador adecuado de acuerdo con ciertas condiciones, y luego enviar esta tarea a este trabajador, y dejar que ayude a enviar la tarea al clúster para que se ejecute. Cómo elegir al trabajador adecuado no es el enfoque de este momento, continuemos para ver cómo ejecutar la tarea actual.

/**
     * task dispatch
     *
     * @param context context
     * @return result
     * @throws ExecuteException if error throws ExecuteException
     */
    public Boolean dispatch(final ExecutionContext context) throws ExecuteException {
    
    
        /**
         * get executor manager
         */
        ExecutorManager<Boolean> executorManager = this.executorManagers.get(context.getExecutorType());
        if(executorManager == null){
    
    
            throw new ExecuteException("no ExecutorManager for type : " + context.getExecutorType());
        }

        /**
         * host select
         */

        Host host = hostManager.select(context);
        if (StringUtils.isEmpty(host.getAddress())) {
    
    
            throw new ExecuteException(String.format("fail to execute : %s due to no suitable worker , " +
                            "current task need to %s worker group execute",
                    context.getCommand(),context.getWorkerGroup()));
        }
        context.setHost(host);
        executorManager.beforeExecute(context);
        try {
    
    
            /**
             * task execute
             */
            return executorManager.execute(context);
        } finally {
    
    
            executorManager.afterExecute(context);
        }
    }

4. Objeto NettyExecutorManager.

Esta vez tengo que confiar el objeto NettyExecutorManager para ayudar a realizar esta tarea. Echemos un vistazo a la lógica específica del código de ejecución.

@Override
    public Boolean execute(ExecutionContext context) throws ExecuteException {
    
    

        /**
         *  all nodes
         */
        Set<String> allNodes = getAllNodes(context);

        /**
         * fail nodes
         */
        Set<String> failNodeSet = new HashSet<>();

        /**
         *  build command accord executeContext
         */
        Command command = context.getCommand();

        /**
         * execute task host
         */
        Host host = context.getHost();
        boolean success = false;
        while (!success) {
    
    
            try {
    
    
                doExecute(host,command);
                success = true;
                context.setHost(host);
            } catch (ExecuteException ex) {
    
    
                logger.error(String.format("execute command : %s error", command), ex);
                try {
    
    
                    failNodeSet.add(host.getAddress());
                    Set<String> tmpAllIps = new HashSet<>(allNodes);
                    Collection<String> remained = CollectionUtils.subtract(tmpAllIps, failNodeSet);
                    if (remained != null && remained.size() > 0) {
    
    
                        host = Host.of(remained.iterator().next());
                        logger.error("retry execute command : {} host : {}", command, host);
                    } else {
    
    
                        throw new ExecuteException("fail after try all nodes");
                    }
                } catch (Throwable t) {
    
    
                    throw new ExecuteException("fail after try all nodes");
                }
            }
        }

        return success;
    }
Ignore otros métodos doExecute que continúan persiguiéndolo, que es enviar la información del comando con la ayuda de nettyRemoteClient.
private void doExecute(final Host host, final Command command) throws ExecuteException {
    
    
        /**
         * retry count,default retry 3
         */
        int retryCount = 3;
        boolean success = false;
        do {
    
    
            try {
    
    
                nettyRemotingClient.send(host, command);
                success = true;
            } catch (Exception ex) {
    
    
                logger.error(String.format("send command : %s to %s error", command, host), ex);
                retryCount--;
                ThreadUtils.sleep(100);
            }
        } while (retryCount >= 0 && !success);

        if (!success) {
    
    
            throw new ExecuteException(String.format("send command : %s to %s error", command, host));
        }
    }
El próximo artículo analizará los detalles de nettyclient que envía tareas a los trabajadores.

Supongo que te gusta

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