如何 运行 在另一个线程上阻塞代码并立即发出 http 请求 return

How to run blocking codes on another thread and make http request return immediately

我们用 QuarkusMutiny 开始了一个新项目,并用 Quarkus @Funq 创建了一堆端点,到目前为止一切正常。现在我们想在其中一个端点中处理一些非常耗时的事情,而我们期望的是,一旦用户单击按钮从前端发送 http 请求并点击该特定端点,我们将 return 202 Accepted 立即,将耗时的操作处理留在后端的另一个线程中,然后在完成后相应地发送通知邮件给用户。

我知道这可以用 @AsyncCompletableFuture 来完成,但现在我们想用 Mutiny 来完成。根据我在这里阅读 Mutiny 文档的方式 https://smallrye.io/smallrye-mutiny/guides/imperative-to-reactiverunSubscriptionOn 将通过 运行 在另一个线程上使用耗时的方法来避免阻塞调用者线程,我的测试表明耗时的代码确实在不同的线程上执行。但是,http 请求会立即执行 not return,直到耗时的方法完成执行(正如我在浏览器的开发人员工具中观察到的那样),它仍然处于等待状态。我是否误解了 runSubscriptionOn 的工作原理?如何使用 Mutiny 实现此功能?

我的 @Funq 端点看起来像这样

@Inject
MyService myService;

@Funq("api/report")
public Uni<String> sendReport(MyRequest request) {
    ExecutorService executor = Executors.newFixedThreadPool(10, r -> new Thread(r, "CUSTOM_THREAD"));

    return Uni.createFrom()
        .item(() -> myService.timeConsumingMethod(request))
        .runSubscriptionOn(executor);
} 

编辑:我根据@Ladicek 的回答找到了使用Uni 的解决方案。在深入研究 Quarkus 和 Uni 之后,我有一个后续问题:

目前我们的大多数阻塞方法都是 not returning Uni 在 Service 级别,而不是我们从它们 return 的内容创建 Uni 对象(即对象或列表), 和 return Uni on Controller level in their endpoints like this

return Uni.createFrom().item(() -> myService.myIOBlockingMethod(request)).

正如@Ladicek 所解释的那样,我不必显式使用 .runSubscriptionOn,因为 IO 阻塞方法会在工作线程上自动 运行(因为我在服务级别的方法 not return大学)。这有什么缺点吗?我的理解是,这会导致更长的响应时间,因为它必须在 I/O 线程和工作线程之间跳转,对吗?

这方面的最佳做法是什么?对于 Service 级别上的那些阻塞方法,我是否应该始终 return Uni 以便它们也可以 运行 在 I/O 线程上?如果是这样,我想我总是需要在不同的工作线程上调用 .runSubscriptionOn 到 运行 它,这样 I/O 线程就不会被阻塞,对吗?

通过 returning a Uni,您基本上是在说 Uni 完成时响应完成。您想要的是 运行 线程池上的操作和 return 完整的响应(Uni 与否,这无关紧要)。

顺便说一下,您在方法中为每个请求创建了一个额外的线程池,并且不要关闭它。那是错误的。您希望为所有请求创建一个线程池(例如,在 @PostConstruct 方法中)并且理想情况下在应用程序结束时也将其关闭(在 @PreDestroy 方法中)。