2

背景:我这周才刚刚开始使用 Quarkus,尽管我之前使用过一些流媒体平台(尤其是 scala 中的 http4s/fs2)。

使用 quarkus reactive(与 mutiny)和任何响应式数据库客户端(mutiny reactive postgres、reactive elasticsearch 等)我有点困惑如何正确管理阻塞调用和线程池。

quarkus 文档建议使用命令式代码或 cpu 密集型代码进行注释,@Blocking以确保将其转移到工作池而不阻塞 IO 池。这是有道理的。

考虑以下:

public class Stuff {
  // imperative, cpu intensive
  public static int cpuIntensive(String arg) { /* ... */ }

  // blocking IO
  public static List<Integer> fetchFromDb() { /* ... */ }

  // reactive IO
  public static Multi<String> fetchReactive() { /* ... */ }

  // reactive IO with CPU processing
  public static Multi<String> fetchReactiveCpuIntensive(String arg) {
    fetchReactive() // reactive fetch
      .map(fetched -> cpuIntensive(arg + fetched)) // cpu intensive work
  }
}

我不清楚在上述每种情况下会发生什么,以及如果没有注释的 resteasy-reactive 休息端点调用它们,它们会在哪里执行@Blocking


据推测,在没有@Blocking. 但是,Uni对于“不安全”的代码来说,包装一个阻塞调用是否同样有效?也就是说,任何返回Multi/的东西都会在池中Uni有效运行吗?worker

(我将打开有关更好地控制线程池的后续帖子,因为我看不到任何方法可以将反应性 IO 调用“转移”到一个单独的池而不是 CPU 密集型工作,这将是最佳的。)

编辑

这个问题可能暗示我在询问返回类型(Uni/Multi与直接对象),但它实际上是关于在任何给定时间选择正在使用的线程池的能力。这个关于命令式到反应式的叛变页面实际上在 某种程度上回答了我的问题,以及叛变基础设施文档,其中指出“默认执行程序已配置为使用 Quarkus 工作线程池。”,叛变线程控制文档处理其余部分我认为。

所以我的理解是这样的:

如果我有一个端点可以有条件地返回非阻塞的东西(例如本地非阻塞缓存命中),那么我可以有效地在 IO 线程上返回任何我想要的方式。但是,如果所述缓存未命中,我可以直接调用响应式客户端或使用 mutiny 对 quarkus 工作池运行阻塞操作。同样,mutiny 提供了在特定线程池(执行程序)上执行任何给定流的控制。

响应式客户端(或在非 IO 池上有效运行的任何东西)可以安全调用,因为 IO 循环只是订阅工作池发出的数据。

最后,似乎我可以将一个 CPU 绑定池与一个 IO 绑定工作池分开配置,并明确地将它们作为executors 提供给我需要的任何发射器。所以......我想我现在已经准备好了。

4

1 回答 1

1

这是一个非常好的问题!

RESTEasy Reactice 端点的返回类型对端点将在哪个线程上提供服务没有任何影响。唯一决定线程的是@Blocking/的存在@NonBlocking

原因很简单:仅使用返回类型,无法知道操作是否真的需要很长时间才能完成(从而阻塞线程)。例如,非响应式返回类型并不意味着该操作是 CPU 密集型的(例如,您可能只是返回一些预设的 JSON 响应)。另一方面,反应类型不能保证操作是非阻塞的,因为正如您所提到的,用户可以简单地用反应返回类型包装阻塞操作。

于 2021-07-04T14:49:10.443 回答