网站首页 > 技术教程 正文
本文介绍如何使用Quarkus(而不是使用同步端点)在Java中实现反应性REST API 。为此,需要Java类CompletableFuture和CompletionStage。并说明如何使用这些类以及如何链接异步方法调用(包括异常处理和超时)。
为什么使用反应性REST API?
我想到的第一个问题是:为什么要改变旧习惯而不使用命令式代码呢?毕竟,对于某些Java开发人员而言,异步代码的实现还是超过很多人预期的,需要重新思考,重新学习,还要不断试错。
我认为答案是效率。我已经运行了两个负载测试,在这些测试中,将反应式代码与命令式代码进行了比较。在这两种情况下,反应性代码的响应时间仅为命令性代码持续时间的一半。尽管这些测试不能代表所有类型的场景,但我认为这个小例子很好地展示了反应式编程的好处。
话虽如此,但我认为不需要将REST API用于所有类型的应用程序。例如,并非每个应用程序都需要高度可扩展。另外,由于可能必须学习新技能并且可能必须扩展经典开发流程,因此响应式应用程序的开发成本可能会更高,企业人力成本,适应成本等各方面都会加强。
第一个反应式REST API
Quarkus 使用Eclipse Vert.x指南提供了一个反应式REST API的例子。为了学习新技术,在遵循入门教程之后,它可以帮忙写一些简单的示例应用程序。这就是为什么我创建了一个示例应用程序,该应用程序可以作为cloud-native-starter项目的一部分提供。
该项目包含一个微服务“文章”,该文章提供了一个REST API,可从数据库返回文章。让我们看一下代码:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.ws.rs.core.Response;
import javax.json.JsonArray;
...
@GET
@Path("/articles")
@Produces(MediaType.APPLICATION_JSON)
public CompletionStage<Response> getArticlesReactive(int amount) {
CompletableFuture<Response> future = new CompletableFuture<>();
articleService.getArticlesReactive(amount).thenApply(articles -> {
JsonArray jsonArray = articles.stream()
.map(article -> articleAsJson.createJson(article))
.collect(JsonCollectors.toJsonArray());
return jsonArray;
}).thenApply(jsonArray -> {
return Response.ok(jsonArray).build();
}).exceptionally(throwable -> {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}).whenComplete((response, throwable) -> {
future.complete(response);
});
return future;
}
如上所示,而不是返回javax.ws.rs.core.Response,而是返回带有Response对象的java.util.concurrent.CompletionStage。另外,在实际业务代码运行之前,会立即返回CompletionStage。运行业务逻辑之后,完成CompletionStage(通过“ CompletionStage.complete”),并将响应传递到API客户端。这种方法的好处是,在运行业务代码时,主线程不会被阻塞。
类java.util.concurrent.CompletableFuture是接口java.util.concurrent.CompletionStage的实现。CompletableFuture还实现java.util.concurrent.Future,以便代码可以等待响应并通过“ CompletableFuture.get”读取响应。另外,CompletableFuture还提供了处理超时的方法,下面将对此进行介绍。
实际的业务逻辑由另一个称为“ ArticlesService”的类提供。“ getArticlesReactive”是一种异步方法,它返回带有文章列表的CompletionStage。一旦提供了响应,就可以使用“ CompletionStage.thenApply”,“ CompletionStage.thenAccept”和“ CompletionStage.thenRun”之类的方法来访问响应。
所有这些方法都再次返回CompletionStage,以便可以像上面的代码片段中那样链接这些方法。“ CompletionStage.thenApply”允许接收输入对象并返回另一个对象(在完成阶段中包装)。在示例中,文章列表转换为JSON数组,此后将数组转换为Response。
链式异步调用和错误处理
“文章”微服务已通过一种干净的体系结构方法实现,其中,微服务的代码分为三个包。这些程序包彼此非常独立,可以与其他实现互换。
- API:包含REST端点并处理传入和传出消息。
- 业务:包含微服务和业务实体的业务逻辑。
- 数据:包含访问数据库或其他微服务的代码。
在该示例中,上方的REST端点位于API层中,并在业务层中调用ArticlesService。
如上所述,调用相当简单。问题是如何处理异常。cloud-native-starter项目还提供REST端点的同步实施。让我们比较一下两种情况下的错误处理方式。
同步版本的ArticlesService具有方法“ getArticles”,该方法引发两个异常(请参见代码):
List<Article> getArticles(int requestedAmount) throws NoDataAccess, InvalidInputParameter {
通常可以在调用该方法的代码中捕获这些异常(请参阅代码):
public Response getArticles(int amount) {
JsonArray jsonArray;
try {
jsonArray = articleService.getArticles(amount).stream().map(article -> articleAsJson.createJson(article)).collect(JsonCollectors.toJsonArray());
return Response.ok(jsonArray).build();
} catch (NoDataAccess e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
} catch (InvalidInputParameter e) {
return Response.status(Response.Status.NO_CONTENT).build();
}
}
对于异步方法,此机制显然不起作用。返回文章的反应式版本ArticlesService不会在接口中声明异常(请参见代码):
public CompletionStage<List<Article>> getArticlesReactive(int requestedAmount) {
if (requestedAmount < 0)
return CompletableFuture.failedFuture(new InvalidInputParameter());
return dataAccess.getArticlesReactive();
}
为了发出异常信号,它返回CompletableFuture并使用实际异常调用'failedFuture'。
使用链接完成阶段时,可以通过“ exceptionally”捕获异常。当抛出(实际)异常或通过'CompletionStage.completeExceptionally'发出异常信号时(参见代码),将执行代码中的这些路径:
}).exceptionally(throwable -> {
future.completeExceptionally(new NoConnectivity());
return null;
});
正如上面的,命令式代码中的异常处理与具有完成阶段的异步代码中的异常处理完全不同。链接完成阶段基本上有两条路径,正常路径和异常路径。如果无法处理异常,则将它们通过“ completeExceptionally”转发到调用代码,并运行异常路径。但是,如果可以处理异常,则流程可以在正常路径中继续。这就是为什么上一片段中的“异常”方法返回null的原因。如果该方法可以处理该异常,则它可以返回一个对象以继续正常路径。
下一个代码片段显示了如何捕获发出信号的异常。在这种情况下,API层中的REST端点实现处理业务层中ArticlesService引起的异常(请参见代码):
articleService.getArticlesReactive(amount).thenApply(articles -> {
...
return jsonArray;
}).thenApply(jsonArray -> {
return Response.ok(jsonArray).build();
}).exceptionally(throwable -> {
if (throwable.getCause().toString().equals(InvalidInputParameter.class.getName().toString())) {
return Response.status(Response.Status.NO_CONTENT).build();
}
else {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}).whenComplete((response, throwable) -> {
future.complete(response);
});
超时处理
异步代码中的异常处理与命令式代码中的异常处理不同。想要编写异步代码的Java开发人员的另一种新模式是必须学习如何处理超时。
当微服务成功调用异步代码时,完成阶段完成时,将调用各种CompletionStage方法,例如“ thenApply”。但是,如果完成阶段从未完成怎么办?在这种情况下,调用代码将永远等待。可以访问数据库或调用其他服务的微服务就是很好的例子。当数据库或服务不可用时,不会停止在用户界面中加载指示器。
这是另一个示例片段,其中异步访问Postgres数据库:
public CompletableFuture<List<Article>> getArticlesReactive() {
CompletableFuture<List<Article>> future = new CompletableFuture<List<Article>>();
client.query("SELECT id, title, url, author, creationdate FROM articles ORDER BY id ASC")
.toCompletableFuture()
.orTimeout(MAXIMAL_DURATION, TimeUnit.MILLISECONDS).thenAccept(pgRowSet -> {
List<Article> list = new ArrayList<>(pgRowSet.size());
for (Row row : pgRowSet) {
list.add(from(row));
}
future.complete(list);
}).exceptionally(throwable -> {
future.completeExceptionally(new NoConnectivity());
return null;
});
return future;
}
为了处理超时,可以使用方法'CompletableFuture.orTimeout'。如果执行时间过长,则会触发“例外”中的代码。请注意,此方法仅在Java 9+中可用。
同样,“ orTimeout”是CompletableFuture的方法,而不是CompletionStage的方法。幸运的是,您可以通过'CompletionStage.toCompletableFuture'将完成阶段转换为可完成的期货。
日常更新编程文正,喜欢就烦请关注一下哦,感谢。
猜你喜欢
- 2024-10-13 gRPC vs REST:两种 API 架构风格的对比
- 2024-10-13 使用GIN,GORM和MySql创建一个Rest API
- 2024-10-13 Elasticsearch应用开发人员应该熟练掌握的REST API
- 2024-10-13 聊聊rest api设计 rest api接口规范
- 2024-10-13 如何为应用设计REST API? 应用设计模式
- 2024-10-13 创建restful风格的API接口很难?明明5分钟就行
- 2024-10-13 WordPress 常用的 REST API接口汇总
- 2024-10-13 一文读懂RESTfulAPI 一文读懂等额本息利率计算,合理规划贷款与还款
- 2024-10-13 前后端程序员都要了解和使用REST API
- 2024-10-13 微软欲停止Outlook REST API身份验证支持,开发者准备好了吗?
你 发表评论:
欢迎- 01-11关于Vmware workstation的网络设置
- 01-11使用VMware Workstation虚拟机安装Windows 10详细教程
- 01-11VMware Workstation 17.5.1 Pro for Windows & Linux - 桌面虚拟化软件
- 01-11VMware? Workstation 17 Pro软件中,如何显示 “我的计算机” 选项卡
- 01-11银河麒麟桌面操作系统安装VMware workstation pro
- 01-11VmwareWorkstation17.6安装windows7x64虚拟机后安装vmtools
- 01-11发布VMware Workstation Pro 17.0稳定版
- 01-11VMware Workstation安装ESXi 7安装篇
- 最近发表
-
- 关于Vmware workstation的网络设置
- 使用VMware Workstation虚拟机安装Windows 10详细教程
- VMware Workstation 17.5.1 Pro for Windows & Linux - 桌面虚拟化软件
- VMware? Workstation 17 Pro软件中,如何显示 “我的计算机” 选项卡
- 银河麒麟桌面操作系统安装VMware workstation pro
- VmwareWorkstation17.6安装windows7x64虚拟机后安装vmtools
- 发布VMware Workstation Pro 17.0稳定版
- VMware Workstation安装ESXi 7安装篇
- 如何安装虚拟机(vmware workstation)
- VMware Workstation之网络配置
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)