4

对于完全非阻塞的端到端响应式调用,是否建议显式调用 publishOn 或 subscribeOn 来切换调度程序?对于 CPU 消耗或非消耗任务,总是使用并行通量来优化性能是否有利?

4

2 回答 2

3

值得说明的是,我假设这里的上下文是 Webflux 而不是一般的反应器(因为问题是这样标记的。)如果我们在不考虑 Webflux 的情况下谈论通用反应器用例,那么建议当然会有很大差异。

对于完全非阻塞的端到端响应式调用,是否建议显式调用 publishOn 或 subscribeOn 来切换调度程序?

一般建议是不要显式调用这些方法,除非您有理由这样做。(在正确的上下文中使用它们并没有错,但这样做“仅仅因为”可能不会带来任何好处。)

对于 CPU 消耗或非消耗任务,总是使用并行通量来优化性能是否有利?

这取决于您要实现的目标,以及“CPU 消耗”(或 CPU 密集型)任务的含义。请注意,我在这里谈论的是真正的CPU 密集型任务,而不是阻塞代码——在这种情况下,我理想地将 CPU 密集型部分分配给另一个微服务,使您能够根据需要进行扩展,与您的 Webflux 服务分开。

使用并行通量(并在并行调度程序上运行)应该使用所有可用的内核来处理数据——这很可能会导致处理得更快。但请记住,默认情况下,您还为每个内核运行一个事件循环,因此您基本上从事件循环中“窃取”了一些可用容量以实现这一目标。这是否理想取决于您的用例,但通常不会带来太多好处。

相反,我推荐两种方法:

  • 如果您可以将 CPU 密集型任务分解为小的、低强度的块,请这样做 - 然后您可以将其保留在事件循环中。这允许事件循环保持及时运行,同时这些 CPU 密集型任务按其可能的方式安排。
  • 如果您无法将其分解,请启动一个单独的调度程序(可以选择具有低优先级,因此不太可能从事件循环中窃取资源),然后将所有 CPU 密集型任务分配给该调度程序。这具有创建更多线程的缺点,但又使事件循环保持空闲。默认情况下,您将拥有与事件循环的内核一样多的线程 - 您可能希望减少线程数,以便为您的“CPU 密集型”调度程序提供更多可使用的内核。
于 2020-05-25T21:56:26.160 回答
2

对于完全非阻塞的端到端响应式调用,是否建议显式调用 publishOn 或 subscribeOn 来切换调度程序?

publishOn当您将数据发布到下游时subscribeOn使用,而当您从上游消费数据时使用。所以这真的取决于你想从事什么样的工作。

对于 CPU 消耗或非消耗任务,总是使用并行通量来优化性能是否有利?

绝对不是,考虑这个例子:

Flux.range(1, 10)
        .parallel(4)
        .runOn(Schedulers.parallel())
        .sequential()
        .elapsed()
        .subscribe(i -> System.out.printf(" %s ", i));

上面的代码完全是浪费,因为i几乎会立即处理。以下代码将比上面的代码执行得更好:

Flux.range(1, 10)
        .elapsed()
        .subscribe(i -> System.out.printf(" %s ", i));

现在考虑一下:

public static <T> T someMethodThatBlocks(T i, int ms) {
    try { Thread.sleep( ms ); }
    catch (InterruptedException e) {}
    return i;
}

// some method here
Flux.range(1, 10)
        .parallel(4)
        .runOn(Schedulers.parallel())
        .map(i -> someMethodThatBlocks(i, 200))
        .sequential()
        .elapsed()
        .subscribe(i -> System.out.printf(" %s ", i));

输出类似于:

 [210,3]  [5,1]  [0,2]  [0,4]  [196,6]  [0,8]  [0,5]  [4,7]  [196,10]  [0,9] 

如您所见,第一个响应是在210ms 之后出现的,然后是 3 个响应,0两者之间的时间接近。该循环一次又一次地重复。这是您应该使用平行助焊剂的地方。请注意,创建更多数量的线程并不能保证性能,因为当有更多数量的线程时,上下文切换会增加很多开销,因此应该在部署之前对代码进行测试。如果有很多阻塞调用,每个 cpu 有超过 1 个线程可能会提高性能,但如果调用是 cpu 密集型的,那么每个 cpu 有多个线程会由于上下文切换而降低性能。

所以总而言之,它总是取决于你想要实现什么。

于 2020-05-25T17:56:17.390 回答