我正在开发一个 Web 应用程序,该应用程序允许其用户选择“在后台”执行长时间运行的进程。一个例子是一些长时间运行的报告生成,或者同时删除数千个对象。
我已经使用定义为 FixedThreadPool 的 ExecutorService 和 ThreadFactory 实现了这一点。 ThreadFactory 是这样构建的:
ThreadFactoryBuilder()
.setNameFormat(clientId + "-BackgroundTask-%d")
.setDaemon(true)
.setPriority(Thread.MIN_PRIORITY)
.build()
我这样执行任务:
Future<TaskStatus> future = clientExecutors.get(clientId).submit(
backgroundTask::execute);
taskFutures.put(backgroundTask.getTaskId(), future);
如何让我的网络服务器始终优先处理新的传入请求(尽可能快)而不是执行后台任务?
换句话说:用户在浏览网站时必须等待很长时间,这永远不应该发生,只是因为有很多后台任务在执行。从上面可以看出,我试图通过设置 .setPriority(Thread.MIN_PRIORITY)
来做到这一点。然而,这似乎还不够。
此外,就目前而言,我已经为 FixedThreadPool 大小设置了一些任意值 (10),并将其全局用于应用程序(及其所有客户)的整个后台处理。
相反,我想为每个客户定义一个线程池,以确保每个客户都有相同的权限在后台运行一定数量的任务。比如说,每个客户都有一个大小为 5 的 FixedThreadPool,在服务器上我将有一个最大值。 50 个不同的客户。这将同时添加多达 250 个正在运行的后台任务。
这里最重要的要求是:这些后台任务需要执行多长时间(例如 2 分钟或 20 分钟)并不重要。重要的是,每个客户都能够发送 5 个任务在后台执行,并且每个任务都平等地处理。
我测试了运行 30 个 CPU 密集型后台任务,结果表明当这些任务正在运行并且 CPU 接近 100% 时,新传入的请求需要很长时间才能处理。
很明显,我做错了。
2017 年 9 月 12 日更新 我读过有关微服务的文章,虽然听起来不错,但我发现从我们的整体应用程序中拆分必要的部分是一个巨大的挑战。主要是因为如果数据选择足够大,几乎每个操作都可能变成一个长时间运行的过程。 此外,我的微服务不会遇到同样的问题吗,即运行微服务的服务器会遭受同样的性能下降。好吧,唯一的好处是,网络应用程序的其余部分将不再受此影响。
我读过一些关于将 Thread.sleep(1) 或 Thread.sleep 通常引入 CPU 密集型操作以减少这些操作中使用的 CPU 量的帖子。我还读到有人将此作为一个方面进行介绍,这样他甚至可以更改动态等待的时间量,以便对将使用多少 cpu 进行一些控制。
然而,我的直觉告诉我这也不对。您如何看待引入 Thread.sleep 来降低用于任务的 CPU 数量?这是常见的做法吗?如果不是,什么是正确的方法?
最佳答案
我会高度考虑更改您的系统架构,以将这些长时间运行的请求卸载到单独的实例,而不是在一般请求服务应用程序的进程中运行它们。总的来说,我认为在同一应用程序实例中处理批处理/在线(或长时间/短期运行)处理是一种反模式。
理想情况下,您会构建一个独立的微服务来处理这些请求,但您也可以简单地部署现有应用程序的 X 个实例,并配置负载均衡器以将请求路由到长时间运行的调用路径(例如 POST/myapp/longrunningjob
) 仅适用于专用于运行这些长时间运行的进程的实例。
关于spring - 如何在执行许多异步后台任务时保持网络服务器响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45922655/