具有CompletableFuture的异步超时

有一天,我重写了执行不佳的多线程代码,该代码在Future.get()某个时刻被阻塞:

public void serve() throws InterruptedException, ExecutionException, TimeoutException {final Future<Response> responseFuture = asyncCode();final Response response = responseFuture.get(1, SECONDS);send(response);
}private void send(Response response) {//...
}

这实际上是一个用Java编写的Akka应用程序,具有1000个线程的线程池(原文如此!)–所有这些都在此get()调用中被阻塞。 否则系统无法跟上并发请求的数量。 重构之后,我们摆脱了所有这些线程,只引入了一个,大大减少了内存占用。 让我们简化一下并显示Java 8中的示例。第一步是引入CompletableFuture而不是普通的Future (请参阅提示9 )。 很简单,如果:

  • 您可以控制如何将任务提交给ExecutorService :只需使用CompletableFuture.supplyAsync(..., executorService)而不是executorService.submit(...)
  • 您处理基于回调的API:使用Promise

否则(如果您已经阻塞了API或Future<T> ),将有一些线程被阻塞。 这就是为什么现在诞生了这么多异步API的原因。 假设我们以某种方式重写了代码以接收CompletableFuture

public void serve() throws InterruptedException, ExecutionException, TimeoutException {final CompletableFuture<Response> responseFuture = asyncCode();final Response response = responseFuture.get(1, SECONDS);send(response);
}

显然,这并不能解决任何问题,我们必须利用新的反应式编程风格:

public void serve() {final CompletableFuture<Response> responseFuture = asyncCode();responseFuture.thenAccept(this::send);
}

这在功能上是等效的,但是现在serve()应该立即运行(没有阻塞或等待)。 只要记住, this::send将在完成responseFuture的同一线程中执行。 如果您不想在某个地方重载某些任意线程池或send()昂贵,请考虑为此使用单独的线程池: thenAcceptAsync(this::send, sendPool) 。 很好,但是我们失去了两个重要的属性:错误传播和超时。 由于我们更改了API,因此错误传播很难。 当serve()方法退出时,异步操作可能尚未完成。 如果您关心异常,请考虑返回responseFuture或其他替代机制。 至少,请记录异常,因为否则它将被吞噬:

final CompletableFuture<Response> responseFuture = asyncCode();
responseFuture.exceptionally(throwable -> {log.error("Unrecoverable error", throwable);return null;
});

请注意上面的代码: exceptionally()尝试从故障中恢复 ,并返回替代结果。 它在这里有效,但是如果您将thenAccept() exceptionally()thenAccept() ,即使在失败的情况下, send()也会被调用,但是参数为null (或者我们从exceptionally()返回的值exceptionally()

responseFuture.exceptionally(throwable -> {log.error("Unrecoverable error", throwable);return null;}).thenAccept(this::send);  //probably not what you think

丢失1秒超时的问题非常微妙。 我们的原始代码等待(阻塞)最多1秒钟,直到Future完成。 否则抛出TimeoutException 。 我们失去了此功能,甚至超时的更糟糕的单元测试也不方便并且经常被跳过。 为了在不牺牲事件驱动精神的前提下实现超时,我们需要一个额外的构建块:在给定时间之后始终失败的未来:

public static <T> CompletableFuture<T> failAfter(Duration duration) {final CompletableFuture<T> promise = new CompletableFuture<>();scheduler.schedule(() -> {final TimeoutException ex = new TimeoutException("Timeout after " + duration);return promise.completeExceptionally(ex);}, duration.toMillis(), MILLISECONDS);return promise;
}private static final ScheduledExecutorService scheduler =Executors.newScheduledThreadPool(1,new ThreadFactoryBuilder().setDaemon(true).setNameFormat("failAfter-%d").build());

这很简单:我们创建一个承诺 (没有基础任务或线程池的未来),并在给定java.time.Duration之后使用TimeoutException完成它。 如果您get()某个地方get()这样的未来,则阻塞了至少那么多时间后,将抛出TimeoutException 。 实际上,它将是ExecutionException包装TimeoutException ,没有办法解决。 请注意,我仅使用一个线程使用固定scheduler线程池。 这不仅是出于教育目的:“在这种情况下,“ 1个线程对于任何人都应该足够 ”” [1]failAfter()本身是没有用的,但是将其与我们的responseFuture结合起来,我们就有了解决方案!

final CompletableFuture<Response> responseFuture = asyncCode();
final CompletableFuture<Response> oneSecondTimeout = failAfter(Duration.ofSeconds(1));
responseFuture.acceptEither(oneSecondTimeout, this::send).exceptionally(throwable -> {log.error("Problem", throwable);return null;});

这里发生了很多事情。 在通过我们的后台任务接收到responseFuture ,我们还创建了一个“合成的” oneSecondTimeout将来,它将永远不会成功完成,但总是在1秒后失败。 现在,我们通过调用acceptEither合并两者。 该运算符将针对第一个完成的将来( responseFutureoneSecondTimeout执行代码块,而只是忽略较慢的代码的结果。 如果asyncCode()内1完成第二this::send将被调用,并从异常oneSecondTimeout会被忽略。 然而! 如果asyncCode()确实很慢,则oneSecondTimeout启动。 但是由于它失败并带有异常,因此将调用exceptionally错误处理程序,而不是this::send 。 您可以认为send()exceptionally都将被调用,而不是两者都被调用。 当然,如果我们有两个正常完成的“普通”期货,则将调用前一个的响应来调用send() ,并丢弃后者。

这不是最干净的解决方案。 一个干净的人会包装原始的未来,并确保它在给定的时间内完成。 此类操作符可在com.twitter.util.Future (Scala;称为com.twitter.util.Futurewithin() )中使用,但是在scala.concurrent.Future丢失(可能是受前者启发)。 让我们留下Scala并为CompletableFuture实现类似的运算符。 它以一个Future作为输入,并返回一个在基础底层完成时完成的Future。 但是,如果完成基础未来花费的时间太长,则会引发异常:

public static <T> CompletableFuture<T> within(CompletableFuture<T> future, Duration duration) {final CompletableFuture<T> timeout = failAfter(duration);return future.applyToEither(timeout, Function.identity());
}

这导致了最终,清洁和灵活的解决方案:

final CompletableFuture<Response> responseFuture = within(asyncCode(), Duration.ofSeconds(1));
responseFuture.thenAccept(this::send).exceptionally(throwable -> {log.error("Unrecoverable error", throwable);return null;});

希望您喜欢这篇文章,因为您可以看到Java中的反应式编程已不再是未来的事情(无双关语)。

翻译自: https://www.javacodegeeks.com/2014/12/asynchronous-timeouts-with-completablefuture.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360935.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js 中对于 css 的变量操作(React也可)

文章目录前言一、设置CSS变量&#xff1f;二、读取变量三、删除变量总结前言 主要讲js 中对于 css 的变量操作&#xff1b; 前端框架&#xff1a;antd框架 一、设置CSS变量&#xff1f; document.body.style.setProperty(--primary, #7F583F’);二、读取变量 document.body…

ASIHTTPRequest类库简介和使用说明

官方网站&#xff1a; http://allseeing-i.com/ASIHTTPRequest/ 。可以从上面下载到最新源码&#xff0c;以及获取到相关的资料。 使用iOS SDK中的HTTP网络请求API&#xff0c;相当的复杂&#xff0c;调用很繁琐&#xff0c;ASIHTTPRequest就是一个对CFNetwork API进行了封装&a…

UltraESB的首选IDE – IntelliJ IDEA

在AdroitLogic&#xff0c;我们长期以来一直在使用IntelliJ IDEA进行开发。 它是Java和相关语言/技术的最佳IDE&#xff08;它可能也是许多其他语言的选择&#xff0c;但我的经验主要是Java和相关技术&#xff09;。 Groovy和IDEA的Grails的集成很棒。 通过自动发现JDBC驱动程…

跟我一步一步开发自己的Openfire插件

这篇是简单插件开发&#xff0c;下篇聊天记录插件。 开发环境&#xff1a; System&#xff1a;Windows WebBrowser&#xff1a;IE6、Firefox3 JavaEE Server&#xff1a;tomcat5.0.2.8、tomcat6 IDE&#xff1a;eclipse、MyEclipse 8开发依赖库&#xff1a; Jdk1.6、jasper-com…

React div加载背景图

backgroundImage: url(${ActivitiesBack})

Apache FOP与Eclipse和OSGi的集成

Apache FOP是由XSL格式化对象&#xff08; XSL-FO &#xff09;驱动的开源打印处理器。 例如&#xff0c;将数据对象转换为PDF可能非常有用。 但是&#xff0c;将其集成到PDE中并最终以OSGi Service的形式运行并最终显得有些麻烦。 因此&#xff0c;我提供了一个P2存储库&…

预览视频

window.URL.createObjectURL(files[0].file),

C++ 四

//运算符函数&#xff0c; 重载。 #include<iostream> using namespace std; class A{ int data; public: A(int d0):data(d){ } void show(){ cout<<"data1"<<data<<endl; } //friend A operator-(const A& a1,const A& a2);//授权…

不删除侦听器–使用ListenerHandles

听一个可观察的实例并对它的变化做出反应很有趣。 做一些必要的事情来打断或结束这种聆听会变得很有趣。 让我们看看问题的根源和解决方法。 总览 这篇文章将首先讨论这种情况&#xff0c;然后再讨论常见的方法和问题所在。 然后&#xff0c;它将提供解决大多数问题的简单抽象…

将url参数字符串转成数组

const url"/BaseDictionary?Type34"; const arrurl.split(?); // arr["/BaseDictionary","Type34"]; typeStr parse(arr[1]); // typeStr{Type: "34"}

uniapp /deep/设置uni-app组件样式时 h5生效 小程序失效问题解决

今天写uni-app的项目 设置uni-app扩展组件的样式 使用穿透/deep/ 发现小程序没有效果 h5有效果 //小程序无效 h5生效 /deep/ .uni-list-item .uni-list-item__container .uni-list-item__content .uni-list-item__content-title{color: #333333;font-size: 32upx;}加入一下代…

使用Google Guava Cache进行本地缓存

很多时候&#xff0c;我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据。 在涉及网络呼叫的情况下&#xff0c;将存在固有的网络等待时间&#xff0c;网络带宽限制。 解决此问题的方法之一是在应用程序本地拥有一个缓存。 如果您的应用程序跨越多个节点&…

uva 1394poj 3517

递推&#xff0c;把问题转化为具有相同问题的子问题&#xff0c;通过子问题最后所剩余的编号&#xff0c;退出此问题所剩余的编号 #include <iostream> using namespace std; const int maxn1000010; int f[maxn]; int main() {int n,k,m;while(~scanf("%d %d %d&qu…

父级和子级div的点击事件相互影响

解决方法&#xff1a;event.stopPropagation();

Jersey和Spring Boot入门

除了许多新功能&#xff0c;Spring Boot 1.2还带来了Jersey支持。 这是吸引喜欢标准方法的开发人员的重要一步&#xff0c;因为他们现在可以使用JAX-RS规范构建RESTful API&#xff0c;并将其轻松部署到Tomcat或任何其他Springs Boot支持的容器中。 带有Spring平台的Jersey可以…

js对象数组(JSON) 根据某个共同字段分组

希望的是将下面的对象数组&#xff1a; [{"id":"1001","name":"值1","value":"111"},{"id":"1001","name":"值1","value":"11111"},{"id&quo…

用装饰器改变收藏

装饰图案 自从第一次学习编程设计模式以来&#xff0c;装饰器模式一直是我的最爱。 在我看来&#xff0c;这是一个很新颖的想法&#xff0c;比其他想法有趣得多。 不要误会我的意思&#xff0c;其他大多数人也引起了我的注意&#xff0c;但没有什么比装饰器模式更重要。 至今&a…

ASP.NET WebAPI 自定义ControllerSelector

呃..今天同事要实现客户端调用不同版本Controller的功能, 其实几句代码就搞定了.. 首先定义自己的ControllerSelector,代码如下: public class ShadowControllerSelector : IHttpControllerSelector{private readonly HttpConfiguration _configuration;public ShadowControlle…

MomentJS计算两个时间的差值diff方法

moment(endTime).diff(moment(startTime), years)moment(endTime).diff(moment(startTime), months)moment(endTime).diff(moment(startTime), days) // 开始时间和结束时间的时间差&#xff0c;以“天”为单位&#xff1b;endTime和startTime都是毫秒数moment(endTime).d…

JAX-RS 2.0:服务器端处理管道

这篇文章的灵感来自JAX-RS 2.0规范文档 &#xff08;附录C&#xff09;中的Processing Pipeline部分。 我喜欢它是因为它提供了JAX-RS中所有模块的漂亮快照-以准备好吞咽的胶囊形式&#xff01; 礼貌– JAX-RS 2.0规范文档 因此&#xff0c;我想到了使用此图简要概述不同的JA…