为了账号安全,请及时绑定邮箱和手机立即绑定

Spring - 添加低优先级多线程服务(对生产性能没有影响)

Spring - 添加低优先级多线程服务(对生产性能没有影响)

holdtom 2023-07-19 16:11:54
我们有一个 Spring 应用程序,我想添加一个服务,该服务将使用多个线程处理 10Ks ID,但将作为后台进程而不影响生产实时性。服务将更新数据库并发送外部提供商请求。我不希望服务影响生产性能/计时,我想以低优先级对每个 ID 执行操作我希望对超出此特定执行器范围的所有其他线程设置低优先级。答案使用ThreadPoolExecutor与我的案例更相关吗?ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, numOfWorkerThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());threadPool.setThreadFactory(new OpJobThreadFactory(Thread.NORM_PRIORITY-2));public final static class OpJobThreadFactory implements ThreadFactory { private int priority; public OpJobThreadFactory(int priority) {  this(priority, true);}@Overridepublic Thread newThread(Runnable r) {  Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());  t.setDaemon(daemon);  t.setPriority(priority); }}也许甚至使用Thread.MIN_PRIORITY或者我可以使用Executors.newCachedThreadPool()创建一个线程池,该线程池根据需要创建新线程,但会重用以前构造的线程(当它们可用时)。这些池通常会提高执行许多短期异步任务的程序的性能。我还应该使用Spring bean吗?因为我需要按需/请求创建池,所以似乎不需要/错误编辑 我应该使用Spring Actuator来获取此任务还是其他监控工具?Spring Boot Actuator 模块通过提供健康检查、审计、指标收集、HTTP 跟踪等生产就绪功能来帮助您监控和管理 Spring Boot 应用程序。所有这些功能都可以通过 JMX 或 HTTP 端点访问。
查看完整描述

2 回答

?
达令说

TA贡献1821条经验 获得超6个赞

我想澄清一下这个问题

什么是线程优先级?根据 java SE 文档

每个线程都有一个优先级。具有较高优先级的线程优先于具有较低优先级的线程执行。

即使您创建具有优先级的线程,它也不能完全保证优先级较低的线程首先执行,您可能必须阻塞优先级较低的线程,直到执行其他线程

对于小型 java 程序,您可以自己处理线程执行,但对于较大的程序,建议您使用 vanilla Java 中的 Executor Framework(来自 java.util.concurrent 包)或使用 spring TaskExecutor。通过使用这两个框架,您可以异步执行任务,即作为主任务的一部分在后台执行它们。

对生产的影响:

例如,主要任务将是调用您的其余端点(即/account),并在调用帐户端点时您想要向客户发送欢迎电子邮件,这是第三方 API 调用,可以使用 Executor Framework 或 Spring 异步执行TaskExecutor 异步执行它们,即作为后台进程,它们不会对当前 API 产生影响,但肯定会对生产服务器产生影响,因为您在同一个 JVM 中运行线程并且它们共享公共内存。如果创建了许多线程但没有销毁,那么服务器肯定会崩溃。

因此,使用 Executor Framework 或 Spring TaskExecutor 并不能保证它不会影响您当前的生产,它肯定会提高所调用的其余 API 的性能。因为它是异步执行的并且关于您的其他问题

我可以使用 Executors.newCachedThreadPool()

是的,如果您有许多短期任务,例如更新数据库中的单个列或仅触发一次休息端点,并且它不适合批量加载或执行某些更新 10000 条记录的后端作业,因为它会创建更大的数量每个任务都有多个线程,您肯定会遇到内存问题。

查看完整回答
反对 回复 2023-07-19
?
慕莱坞森

TA贡献1810条经验 获得超4个赞

这是一种创建可配置任务“堆”并始终将主任务保留在堆顶部的方法。

总结这个过程,您应该创建一个自定义任务执行器。首先,您需要创建一个 ThreadPoolTaskExecutor bean,并覆盖一个方法。需要修改的属性有:CorePoolSize(初始线程数)、QueueCapacity(队列中等待的线程数)和MaxPoolSize(最大线程数)。使用这些参数,您可以配置应用程序限制,以使该服务不会影响生产性能。

 @Bean("CustomTaskExecutor")

  public TaskExecutor threadPoolTaskExecutor(

          @Value("${spring.async.core-pool-size}") int corePoolSize,

          @Value("${spring.async.max-pool-size}") int maxPoolSize,

          @Value("${spring.async.queue-capacity}") int queueCapacity) {

    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {


      @Override

      protected BlockingQueue<Runnable> createQueue(int queueCapacity) {

        return new PriorityBlockingQueue<Runnable>(queueCapacity);

      }


    };

    executor.setCorePoolSize(corePoolSize);

    executor.setMaxPoolSize(maxPoolSize);

    executor.setQueueCapacity(queueCapacity);

    return executor;

  }

之后,您需要制定任务执行者可以理解的优先级。为此,我们需要创建两个类: 1) 一个实现 Runnable 接口的自定义类,该类将运行任务 2) 一个扩展 FutureTask 并实现 Comparable 接口的包装类,以便任务执行器可以理解任务的优先级选择逻辑任务


public class Task implements Runnable {

    private Consumer<Job> jobConsumer;

    private Job job;

      public Job getJob() {

        return this.job;

      }

      public Task(Consumer<Job> jobConsumer, Job job) {

        this.jobConsumer = jobConsumer;

        this.job = job;

      }

      @Override

      public void run() {

        this.jobConsumer.accept(job);

      }

    }

然后你就有了 FutureCustomTask 类:


public class FutureCustomTask extends FutureTask<FutureCustomTask> implements Comparable<FutureCustomTask> {

private Task task;

public FutureCustomTask(Task task) {

    super(task, null);

    this.task = task;

  }

@Override

  public int compareTo(FutureCustomTask o) {

    return task.getJob().getPriority().compareTo(o.task.getJob().getPriority());

  }

}

为了执行任务执行器需要自动装配。然后,您可以创建 Task 对象,将其包装在 FutureCustomTask 中,并将其传递给 TaskExecutor。代码应如下所示:


@Autowired

private TaskExecutor taskExecutor;

@Autowired

private JobBusiness jobBusiness;

...

Task task = new Task(jobBusiness::performSomethingOn, job);

taskExecutor.execute(new FutureCustomTask(task));


查看完整回答
反对 回复 2023-07-19
  • 2 回答
  • 0 关注
  • 138 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信