我正在使用Project Reactor编写新服务。我正在使用Netty的Spring 5。我正在与许多不同的服务和关系数据库进行交互。所有这些服务都有一个正在阻止的客户端,并且JDBC也正在阻止。因此,基本上所有这些网络调用都不会返回发布者。
我的问题是使用所有阻塞API时的最佳做法是什么。目前,我所做的是将所有阻塞的API代码包装到Mono.callable中,并使用了subscriptionOn(Scheduler.elastic())。因此,基本上所有阻塞工作都是在弹性线程池上完成的。
问题1.我是否应该创建专用的线程池执行程序,而不是使用Elastic?如果是,那为什么呢?还是应该为每个不同的服务创建一个专用的弹性池?
问题2.当阻塞方法返回结果时,我应该使用publishOn以便主线程再次进行处理吗? 如果是,如何获取主线程(netty事件循环线程?)引用?
问题3.如果我要在多个不同的运算符中调用多个阻塞调用(有的在链接时,有的在使用zip()调用时),然后使用订阅发布和再次订阅发布不会带来很多上下文切换?
这些问题大多数与最佳实践有关。
答案 0 :(得分:1)
这些问题大多数与最佳实践有关。
反应性Java仍处于起步阶段,因此很难找到公认的“最佳实践”(大多数情况下,整个社区尚未形成共识,因此反应堆开发团队和其他一些。)
话虽如此,我认为现在有一些合理的,被广泛接受的观点将对此有所帮助。首先,您声明:所有这些服务的客户端都被阻止。
这可能是最简单的方法,因为如果您未与也非阻塞的大量其他服务进行交互,那么使用反应堆根本没有意义。如果您有明确的迁移路径将这些服务重写为非阻塞,则为例外。
JDBC也正在阻止
R2DBC不是,可能值得调查(尽管该项目还处于初期阶段。)
我应该创建一个专用的线程池执行程序,而不是使用Elastic吗?
没有必要。 (答案是“是”的唯一原因是,如果您有非常特定的线程要求,需要对该执行程序服务进行特定的“调整”,但是如果是这种情况,则不会问这个问题。)
请考虑使用boundedElastic()
,因为这会限制调度程序中活动线程的数量,这可以减少产生背压问题的线程数量失控。该调度程序经过专门设计,可与遗留的阻塞IO库建立接口。
我应该为每个不同的服务创建一个专用的弹性池吗?
除非您有特殊原因,否则不要。如果您想为不同的服务设置单独的(或不同的)绑定,则在boundedElastic()
调度程序中这将更为相关。 (恕我直言,仅仅创建一个以便您可以“给它一个相关的名称”还不够好。)
当阻塞方法返回结果时,是否应该使用publishOn以便主线程再次进行处理?
这完全取决于您的用例-Reactor有意并发不可知,因此它取决于您是否需要在“主线程”中发布结果。 (以我的经验,通常你不在乎。)
如何获取主线程引用?
publishOn(Schedulers.immediate())
可用于在您正在调用的当前线程上发布。
如果我要在多个不同的运算符中调用多个阻塞调用(有的在链接时,有的在使用zip()调用时),然后使用subscribe publish和再次进行subscribe publish不会带来很多上下文切换?
这取决于。但是,是的,当然有可能-循环回到我的第一点。除非您花费在非阻塞代码上的时间超过在线程池中调用阻塞服务所花费的时间,否则切换的动力就很小(除非您计划在核心就绪时逐步将服务迁移到非阻塞)。
从仅切换到Reactor并将其用作前端到阻塞封装在线程池中的服务,您肯定不会获得性能增强。
答案 1 :(得分:0)
由于您的所有端点服务都处于阻塞状态,因此我不确定在特定情况下使用WebFlux
可能会带来的好处。
1 :基本上,您应该根据自己的特定硬件和目标行为做出决定。正如官方文档所述,Java Reactor
是并发不可知的。这意味着您可以随意使用任何并发或根本不使用它。据我所知,elastic pool
在幕后使用普通的ExecutorService
。我猜您应该根据您的特定硬件做出决定,从性能的角度来看,太多的上下文切换和线程是不好的,特别是如果您没有足够的处理器
2 :基本上不,取决于您需要的行为。
3:您应该更加清楚自己,我不明白您为什么称这样的链publish
-subscribe
。