Método de implementación del grupo de subprocesos de prioridad de JVM como cola de tareas

Prefacio

Todos sabemos que la mayor parte del trabajo de los servicios web es aceptar solicitudes http y devolver los resultados procesados. Cada solicitud aceptada por el servidor puede verse como una tarea. En términos generales, estas tareas solicitadas se procesarán de manera ordenada de acuerdo con el orden de las solicitudes. Si el procesamiento de las tareas solicitadas requiere mucho tiempo, a menudo es necesario ponerlas en cola. Al mismo tiempo, diferentes tareas pueden tener directamente algunos cambios de prioridad. En este momento, es necesario introducir y administrar la cola de tareas. Hay muchas cosas que se pueden usar como colas de tareas, como el grupo de subprocesos que viene con Java y otro middleware de mensajes.

Sincrónico y asincrónico

Esta pregunta ha sido mencionada muchas veces antes, algunas tareas requieren resultados inmediatos después de ser solicitadas, mientras que otras no. Imagine un escenario en el que realiza un pedido de compra. Después de pagar, el sistema solo necesita devolver un pago exitoso. Los siguientes aumentos de puntos, emisión de cupones, arreglos de entrega, etc.no necesitan devolverse al usuario en tiempo real. Estos son tareas asincrónicas. Un gran número de tareas asincrónicas llegan a nuestro servicio desplegado, debido al cuello de botella en la eficiencia del procesamiento, no se puede lograr el procesamiento en tiempo real, por lo que es necesario almacenarlas temporalmente en una cola para procesarlas en una cola.

Grupo de hilos

Al mencionar las colas en Java, además de pensar en estructuras de datos básicas, debería haber grupos de subprocesos. El grupo de subprocesos viene con un conjunto de mecanismos que pueden realizar la ejecución y puesta en cola de tareas, que pueden cumplir con la mayoría de los escenarios asincrónicos en un entorno de un solo punto. El siguiente es un flujo de procesamiento típico:

// 注入合适类型的线程池
@Autowired
private final ThreadPoolExecutor asyncPool;
@RequestMapping(value = "/async/someOperate", method = RequestMethod.POST)
public RestResult someOperate(HttpServletRequest request, String params,String callbackUrl {
  // 接受请求后 submit 到线程池排队处理
  asyncPool.submit(new Task(params,callbackUrl);
  return new RestResult(ResultCode.SUCCESS.getCode(), null) {
   
   {
    setMsg("successful!" + prop.getShowMsg());
  }};
}

// 异步任务处理
@Slf4j
public class Task extends Callable<RestResult> {
  private String params;
  private String callbackUrl;
  private final IAlgorithmService algorithmService = SpringUtil.getBean(IAlgorithmServiceImpl.class);
  private final ServiceUtils serviceUtils = SpringUtil.getBean(ServiceUtils.class);
  public ImageTask(String params,String callbackUrl) {
    this.params = params;
    this.callbackUrl = callbackUrl;
  }

  @Override
  public RestResult call() {
    try {
      // 业务处理
      CarDamageResult result = algorithmService.someOperate(this.params);
      // 回调
      return serviceUtils.callback(this.callbackUrl, this.caseNum, ResultCode.SUCCESS.getCode(), result, this.isAsync);
    } catch (ServiceException e) {
      return serviceUtils.callback(this.callbackUrl, this.caseNum, e.getCode(), null, this.isAsync);
    }
  }
}

Para el grupo de subprocesos, no entraré en detalles aquí, solo discutiré brevemente el proceso específico:

  • Después de recibir la solicitud, los parámetros se pasan al grupo de subprocesos y se ponen en cola después de la verificación.
  • Devolver resultado: "La solicitud se realizó correctamente y se está procesando".
  • Una vez que la tarea se pone en cola, el subproceso correspondiente la procesa y la devolución de llamada de la interfaz se realiza una vez finalizado el procesamiento.

El ejemplo anterior describe un modelo en el que la velocidad de producción es mucho más rápida que la velocidad de consumo. Generalmente, las aplicaciones de nivel empresarial desarrolladas para bases de datos. Debido a la gran cantidad de conexiones desarrolladas por el grupo de conexiones de la base de datos, generalmente no es necesario procesar a través de el grupo de subprocesos de esta manera, mientras que algunas GPU En escenarios de aplicaciones intensivas, cuando la velocidad de consumo es lenta debido al cuello de botella de la memoria de video, se requiere la cola para realizar ajustes.

Grupo de subprocesos prioritarios

Más complicado, por ejemplo, considerando la prioridad de la tarea, el grupo de subprocesos debe reescribirse y la cola de bloqueo predeterminada se reemplaza por PriorityBlockingQueue. Vaya directamente al código.

import lombok.Data;

import java.util.concurrent.Callable;

/**
 * @author Fururur
 * @create 2020-01-14-10:37
 */
@Data
public abstract class PriorityCallable<T> implements Callable<T> {
  private int priority;
}
import lombok.Getter;
import lombok.Setter;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 优先级线程池的实现
 *
 * @author Fururur
 * @create 2019-07-23-10:19
 */
public class PriorityThreadPoolExecutor extends ThreadPoolExecutor {

  private ThreadLocal<Integer> local = ThreadLocal.withInitial(() -> 0);

  public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue());
  }

  public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                   long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue(), threadFactory);
  }

  public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                   long keepAliveTime, TimeUnit unit, RejectedExecutionHandler handler) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue(), handler);
  }

  public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                   long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory,
                   RejectedExecutionHandler handler) {
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, getWorkQueue(), threadFactory, handler);
  }

  private static PriorityBlockingQueue getWorkQueue() {
    return new PriorityBlockingQueue();
  }

  @Override
  public void execute(Runnable command) {
    int priority = local.get();
    try {
      this.execute(command, priority);
    } finally {
      local.set(0);
    }
  }

  public void execute(Runnable command, int priority) {
    super.execute(new PriorityRunnable(command, priority));
  }

  public <T> Future<T> submit(PriorityCallable<T> task) {
    local.set(task.getPriority());
    return super.submit(task);
  }

  public <T> Future<T> submit(Runnable task, T result, int priority) {
    local.set(priority);
    return super.submit(task, result);
  }

  public Future<?> submit(Runnable task, int priority) {
    local.set(priority);
    return super.submit(task);
  }

  @Getter
  @Setter
  protected static class PriorityRunnable implements Runnable, Comparable<PriorityRunnable> {
    private final static AtomicLong seq = new AtomicLong();
    private final long seqNum;
    private Runnable run;

    private int priority;

    PriorityRunnable(Runnable run, int priority) {
      seqNum = seq.getAndIncrement();
      this.run = run;
      this.priority = priority;
    }

    @Override
    public void run() {
      this.run.run();
    }

    @Override
    public int compareTo(PriorityRunnable other) {
      int res = 0;
      if (this.priority == other.priority) {
        if (other.run != this.run) {
          // ASC
          res = (seqNum < other.seqNum ? -1 : 1);
        }
      } else {
        // DESC
        res = this.priority > other.priority ? -1 : 1;
      }
      return res;
    }
  }
}

Los puntos principales son los siguientes:

  • Reemplace la cola de bloqueo predeterminada del grupo de subprocesos con PriorityBlockingQueue, y la clase de subproceso entrante en respuesta debe implementar Comparable para la comparación.
  • La estructura de datos de PriorityBlockingQueue determina que las tareas con la misma prioridad no pueden garantizar FIFO y necesitan controlar el orden por sí mismas.
  • El método execute () del grupo de subprocesos debe reescribirse. Si ha leído el código fuente del grupo de subprocesos, encontrará que después de ejecutar el método submit (tarea), se convertirá a RunnableFuture y luego se ejecutará. Debido a que la tarea aprobada implementa Comparable, pero el RunnableFuture convertido internamente tiene no se ha implementado, por lo tanto, envíe directamente Throwing Causado por: java.lang.ClassCastException: java.util.concurrent.FutureTask no se puede convertir a java.lang.Comparable tal excepción, por lo que debe reescribir el método execute () para construir un PriorityRunnable como tránsito.

para resumir

El grupo de subprocesos de JVM es la forma más simple y primitiva de implementar colas de tareas asincrónicas.Este artículo presenta el proceso de uso básico y el uso con requisitos de cola de prioridad. Este método puede cumplir con algunos escenarios comerciales simples, pero también tiene ciertas limitaciones:

  • El grupo de subprocesos de JVM es una máquina independiente. Cuando el equilibrio de carga se realiza en la expansión horizontal de varios servicios, habrá varios grupos de subprocesos. Funcionan por separado y no se pueden unificar y administrar bien, por lo que no son adecuados para escenarios distribuidos.
  • El grupo de subprocesos de JVM se basa en la memoria. Una vez que el servicio está inactivo, las tareas se perderán y la confiabilidad será baja.
  • Falta del mecanismo ack como una cola de tareas, una vez que la tarea falla, no se volverá a ejecutar y la cola del grupo de subprocesos no se puede monitorear bien.

Algunas preguntas de entrevistas de alta frecuencia recopiladas en el último 2020 (todas organizadas en documentos), hay muchos productos secos, incluidos mysql, netty, spring, thread, spring cloud, jvm, código fuente, algoritmo y otras explicaciones detalladas, así como planes de aprendizaje detallados, entrevistas Clasificación de preguntas, etc. Para aquellos que necesitan obtener estos contenidos, agregue Q como: 11604713672

Supongo que te gusta

Origin blog.csdn.net/weixin_51495453/article/details/113384213
Recomendado
Clasificación