与Quarkus异步调用REST API

Invoking REST APIs Asynchronously With Quarkus

最近,我写了一篇关于如何使用Quarkus开发响应式REST API的博客。 异步开发实际端点是第一步。 但是,为了最大程度地利用响应功能,完整的代码路径应该是异步的,尤其是持久的操作,例如数据库访问和REST API调用。 本文介绍了两个选项如何与Quarkus异步调用REST API。

两种异步调用REST API的选项

这是两个选项:

  • Eclipse MicroProfile REST客户端

  • Eclipse Vert.x轴Web客户端
  • 问题是何时使用哪个选项。 除了让我自己回答这个问题之外,我还想谈谈Quarkus团队的反应专家Clement Escoffier。 Clement在StackOverflow上回答了这个问题:

  • MicroProfile Rest客户端不是非阻塞的。 Vert.x Web客户端是。

  • 如果您的其余代码使用RX Java,则Vert.x客户端具有简洁的RX Java API。

  • MicroProfile Rest客户端使用注释驱动的方法,Vert.x客户端是API驱动的。
  • 您可以在同一应用程序中使用这两个选项,这是我在cloud-native-starter项目中包含的示例应用程序中所做的。 该示例应用程序使用了几种微服务。 Web-api服务调用了我将在下面描述的articles服务。

    Eclipse Vert.x轴Web客户端

    Quarkus使用Eclipse Vert.x指南描述了如何使用Vert.x客户端。 让我们看一下从web-api服务调用articles服务的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    import io.vertx.axle.core.Vertx;
    import io.vertx.axle.ext.web.client.WebClient;
    import io.vertx.ext.web.client.WebClientOptions;
    import io.vertx.core.json.JsonObject;
    import io.vertx.core.json.JsonArray;
    import javax.annotation.PostConstruct;
    import java.util.concurrent.CompletableFuture;
    ...
    @Inject
    Vertx vertx;
    private WebClient client;
     
    @PostConstruct
    void initialize() {            
      this.client = WebClient.create(vertx, new WebClientOptions().setDefaultHost(ARTICLES_DNS).setDefaultPort(ARTICLES_PORT).setSsl(false));
    }
     
    public CompletableFuture<List<CoreArticle>> getArticlesReactiveVertxWebClient(int amount) {    
      CompletableFuture<List<CoreArticle>> future = new CompletableFuture<>();
      this.client.get("/v2/articles?amount=" + amount)
        .send()
        .toCompletableFuture()
        .orTimeout(MAXIMAL_DURATION, TimeUnit.MILLISECONDS)
        .thenAccept(resp -> {
          if (resp.statusCode() == 200) {
            List<CoreArticle> articles = this.convertJsonToCoreArticleList(resp.bodyAsJsonArray());
            future.complete(articles);
          } else {
            future.completeExceptionally(new NoConnectivity());
          }
        })
        .exceptionally(throwable -> {
          future.completeExceptionally(new NoConnectivity());
          return null;
        });
        return future;
    }

    方法" get"和" send"调用HTTP请求。 我在上一篇文章中解释了为什么要使用" toCompletableFuture"和" orTimeout"。

    请求完成后,必须检查HTTP状态代码并将其转换为Java异常。 如果请求成功,则响应将是io.vertx.core.json.JsonArray或io.vertx.core.json.JsonObject。 这些对象需要手动转换为Java对象,在此示例中为文章列表。

    Eclipse MicroProfile REST客户端

    第二个选项是使用MicroProfile REST客户端。 查阅我以前的博客《从Java Microservices调用REST API》,其中介绍了如何同步调用REST API。

    MicroProfile REST客户端已扩展为也支持异步调用。 有一个OpenLiberty指南,它以异步方式描述了RESTful服务。

    再次,让我们看一下代码。 首先,您需要定义要调用的服务的接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import java.util.concurrent.CompletionStage;
    import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
    ...
    @RegisterProvider(ExceptionMapperArticles.class)
    public interface ArticlesServiceReactive {
      @GET
      @Produces(MediaType.APPLICATION_JSON)
      public CompletionStage<List<CoreArticle>> getArticlesFromService();
    }

    在此接口中,定义了一个异常映射器类。 此类将HTTP错误映射到Java异常(请参见代码):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
    ...
    @Provider
    public class ExceptionMapperArticles implements ResponseExceptionMapper<InvalidArticle> {
      @Override
      public boolean handles(int status, MultivaluedMap<String, Object> headers) {
        return status == 204;
      }
      @Override
      public InvalidArticle toThrowable(Response response) {
        switch (response.getStatus()) {
          case 204:
            return new InvalidArticle();
          }
          return null;
        }
    }

    为了调用REST API,可以调用普通的Java方法。 此方法的实现神奇地处理MicroProfile(请参见代码):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public CompletableFuture<List<CoreArticle>> getArticlesReactive(int amount) {      
      CompletableFuture<List<CoreArticle>> future = new CompletableFuture<>();
      URL apiUrl;
      try {
        apiUrl = new URL("http://" + ARTICLES_DNS +":" + ARTICLES_PORT +"/v2/articles?amount=" + amount);
        ArticlesServiceReactive articlesServiceReative = RestClientBuilder.newBuilder().baseUrl(apiUrl).build(ArticlesServiceReactive.class);
        articlesServiceReative.getArticlesFromService()
          .toCompletableFuture()
          .orTimeout(MAXIMAL_DURATION, TimeUnit.MILLISECONDS)  
          .thenAccept((articles) -> {
            future.complete(articles);
          })
          .exceptionally((throwable) -> {
            future.completeExceptionally(new NoConnectivity());
            return null;
          });
        } catch (MalformedURLException e) {
          future.completeExceptionally(new NoConnectivity());
        }
      return future;
    }

    请注意,将JSON响应反序列化为文章列表是自动完成的。

    总结思想

    我的两分钱:如果您不是经验丰富的Rx Java开发人员,那么我将选择MicroProfile进行异步REST调用。 MicroProfile模型经过精心设计,您无需手动转换对象,并且不会阻塞。

    本文的所有示例都包含在开源项目cloud-native-starter中。 签出查看运行中的代码。

    本文是系列文章的一部分。 阅读本系列的其他文章以了解反应式编程:

  • 用Quarkus开发反应性应用程序

  • 从Quarkus访问Apache Kafka

  • 从Quarkus在Kubernetes中访问PostgreSQL

  • Quarkus的响应消息示例

  • 使用Quarkus开发反应式REST API

  • 比较对Postgres的同步和异步访问