我有一个使用弹簧RestTemplate调出到多个URL的服务。
为了提高性能,我想并行执行这些请求。可我有两个选项:
只是想知道,如果它的最佳实践与阻塞I / O调用使用并行流?
一个ForkJoinPool
是不理想的做IO的工作,因为你没有获得任何的工作特性窃取的好处。如果您计划使用您的应用程序的commonPool
等部位做得比,你可能会干扰他们。一个专门的线程池,例如ExecutorService
,可能是这两个之间的更好的解决方案。
我想建议的东西,甚至更好。而是自己写所有的异步封装代码的,可以考虑使用Spring的AsyncRestTemplate
。它包含在Spring的Web库及其API几乎是相同的RestTemplate
。
Spring的核心类异步客户端的HTTP访问。公开类似的方法为
RestTemplate
,但回报ListenableFuture
包装,而不是具体的结果。[...]
注意:默认情况下
AsyncRestTemplate
依赖于标准的JDK设施,以建立HTTP连接。可以切换通过使用构造受理AsyncClientHttpRequestFactory
使用例如Apache HttpComponents,Netty的,和OkHttp不同的HTTP库等。
ListenableFuture
实例可以很容易地转换通过CompletableFuture
到ListenableFuture::completable()
实例。
如同在Javadoc中指出的那样,你可以控制你想通过指定AsyncClientHttpRequestFactory
使用什么异步机制。有许多内置的实现,对每一所列出的库。在内部,一些图书馆可能会做你的建议,并运行在专用线程池阻塞IO。其他人,如Netty的(如果没有记错),使用非阻塞IO来运行连接。你可能会获得来自一些好处。
然后就看你如何减少的结果。随着CompletableFuture
,您可以访问anyOf
和allOf
佣工和任何的组合实例方法。
例如,
URI exampleURI = URI.create("https://www.stackoverflow.com");
AsyncRestTemplate template = new AsyncRestTemplate/* specific request factory*/();
var future1 = template.exchange(exampleURI, HttpMethod.GET, null, String.class).completable();
var future2 = template.exchange(exampleURI, HttpMethod.GET, null, String.class).completable();
var future3 = template.exchange(exampleURI, HttpMethod.GET, null, String.class).completable();
CompletableFuture.allOf(future1, future2, future3).thenRun(() -> {
// you're done
});
AsyncRestTemplate
已经被弃用,取而代之的Spring Web流量” WebClient
的。这个API是相当不同的,所以我不会去到它(只是说,它确实让你得到一个CompletableFuture
为好)。
Completable未来将是一个更好的方式来做到这一点,因为它在语义上更相关的任务,你可能会保持代码的流动,同时任务收益将。
如果你使用流,与异常中处理,事实上,它是不那么相关的任务,语义在管道lambda表达式的尴尬旁边,你将不得不等待它们全部完成,即使它们是存在的在平行下。为了避免这种情况,你需要期货,但随后你会回来的第一个解决方案。
你可能会考虑混合,使用流来创建期货。但考虑到它是一个阻塞IO组请求,你很可能将没有足够的请求或时间采取并行流的优势,图书馆可能不会分裂并行的任务给你,你将是一个循环更好。