使用Java EE 8中的反应式API加速服务

服务通常可以通过异步处理进行优化,即使不改变其对外界的行为。

某些服务效率不高的原因是,它们需要等待其他服务提供结果才能继续下去。

让我们看一下如何在不等待外部REST服务的情况下调用它们,并独立进行多个并行调用,然后将它们的结果与Java EE 8中的响应管道结合起来。

如果我们的服务调用了多个微服务,并且在每次调用之前都等待每个调用完成并返回结果,那么使用响应式API进行重构是一个很好的选择。 为了提高服务效率,如果它们彼此不依赖,则可以并行执行对外部服务的所有调用。 这将减少等待所花费的时间,从而加快微服务的速度。

为了并行调用REST服务,我们将在JAX-RS中使用新的反应式客户端API。 我们将其与RxJava库结合起来,以在可用时结合其结果。 这种结合将使我们能够编写简洁高效的代码。 另一个好处是,可以释放当前线程以进行进一步处理,同时等待远程调用的结果。

我们将建立一个管道,在结果到达时对其进行处理,最后将其合并为单个响应。 管道的第一部分将调用每个远程服务。 除了等待结果,我们将指定处理每个收到的结果并继续调用其他服务。 在JAX-RS客户端请求构建器上使用rx()方法可以使我们调用get()方法的版本,该版本将立即返回而不是等待结果。 为了处理结果到达时的结果,我们可以将方法处理程序链接到从get()方法的rx版本返回的CompletionStage上:

CompletionStage stage = temperatureServiceTarget
  .request()
  .rx()
  .get(Temperature.class)
  .thenApply(temperature -> new Forecast(temperature));

上面的代码将调用温度服务,然后注册一个lambda表达式,以在到达温度时对其进行处理。 这会将温度映射到预测对象,稍后可以使用stage变量进行访问。

但是,我们希望使用的另一种变体get()方法连同RxJava可流动祈求从泽西岛项目获得Flowable的RxJava而不是CompletionStage 。 与CompletionStage相比, Flowable接口可以更轻松地将多个异步结果与更简单的代码组合在一起,并且效率更高。

使用以下代码,我们将调用外部服务并返回Flowable:

Flowable flowable = temperatureServiceTarget
  .register(RxFlowableInvokerProvider.class)
  .request()
  .rx(RxFlowableInvoker.class)
  .get(Temperature.class)
  .map(temperature -> new Forecast(temperature);

我们注册了额外的RxFlowableInvokerProvider ,它允许以后请求RxFlowableInvoker 。 此调用然后给了我们Flowable从RxJava返回类型。 这些类不在JAX-RS API中,我们必须将它们与Jersey RxJava2库一起添加:

<dependency>
  <groupId>org.glassfish.jersey.ext.rx</groupId>
  <artifactId>jersey-rx-client-rxjava2</artifactId>
  <version>2.26</version>
</dependency>

乍一看,似乎我们在做同一件事的同时使代码变得更加复杂。 但是, Flowable实例使我们能够轻松组合多个调用:

Flowable.concat(flowable1, flowable2)
  .doOnNext(forecast -> {
    forecasts.add(forecast);
  })
  .doOnComplete(() -> {
    asyncResponse.resume(Response.ok(forecasts).build());
  })
  .doOnError(asyncResponse::resume)
  .subscribe();
}

对于从任何流通量收到的每个预测,我们将其添加到预测列表中。 最后,我们将预测列表作为响应发送或发送错误响应。 要注册侦听器,必须最终调用subscribe() ,否则它们将被忽略。

您可能还注意到asyncResponse变量用于发送最终响应或发出错误信号。 这是一个JAX-RS异步响应实例,用于在数据可用时在以后的时间完成REST响应,而不会阻塞初始处理线程。 使用异步响应有助于在等待外部REST服务的结果时节省线程资源。 为了在REST端点中打开异步处理,我们将注入javax.ws.rs.container.AsyncResponse作为REST方法参数,以及@Suspended批注。 我们还将返回类型更改为void,因为我们将使用AsyncResponse实例构建响应:

@GET
@Produces(MediaType.APPLICATION_JSON)
public void getForecasts(@Suspended AsyncResponse asyncResponse) {
  ...here come some asynchronous calls to REST services...
  asyncResponse.resume(...)
}

最终代码示例

以下代码将:

  • 在getForecasts方法中打开REST请求的异步处理
  • 在异步响应上设置5分钟超时
  • 对伦敦和北京执行两次温度服务,而无需等待结果
  • 将结果合并为一系列预测
  • 将序列中的每个预测添加到列表中
  • 处理完所有结果后发送完整列表
  • 发生异常时发送错误结果
  • 使用subscribe方法注册处理程序
private Flowable getTemperature(String location) {
  return temperatureTarget
    .register(RxFlowableInvokerProvider.class)
    .resolveTemplate("city", location)
    .request()
    .rx(RxFlowableInvoker.class)
    .get(Temperature.class)
    .map(temperature -> new Forecast(location, temperature));
}
 
@GET
@Produces(MediaType.APPLICATION_JSON)
public void getForecasts(@Suspended AsyncResponse asyncResponse) {
  List forecasts = new ArrayList<>();
  asyncResponse.setTimeout(5, TimeUnit.MINUTES);
  Flowable.concat(getTemperature("London"), getTemperature("Beijing"))
    .doOnNext(forecast -> {
      forecasts.add(forecast);
    })
  .doOnComplete(() -> {
    asyncResponse.resume(Response.ok(forecasts).build());
  })
  .doOnError(asyncResponse::resume)
  .subscribe();
}

翻译自: https://www.javacodegeeks.com/2018/06/speed-services-reactive-api.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值