{"id":18429105,"url":"https://github.com/openliberty/guide-microprofile-rest-client-async","last_synced_at":"2025-04-07T17:32:37.777Z","repository":{"id":40462078,"uuid":"186434081","full_name":"OpenLiberty/guide-microprofile-rest-client-async","owner":"OpenLiberty","description":"A guide on how to use MicroProfile Rest Client to invoke RESTful microservices asynchronously over HTTP.","archived":false,"fork":false,"pushed_at":"2025-03-05T16:32:48.000Z","size":1064,"stargazers_count":3,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"prod","last_synced_at":"2025-03-22T21:51:09.429Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://openliberty.io/guides/microprofile-rest-client-async.html","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpenLiberty.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-13T14:17:05.000Z","updated_at":"2025-03-05T16:32:52.000Z","dependencies_parsed_at":"2024-03-25T14:14:42.457Z","dependency_job_id":"a14dba95-e1cc-43ad-b2d3-e0815c4fb233","html_url":"https://github.com/OpenLiberty/guide-microprofile-rest-client-async","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-microprofile-rest-client-async","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-microprofile-rest-client-async/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-microprofile-rest-client-async/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-microprofile-rest-client-async/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpenLiberty","download_url":"https://codeload.github.com/OpenLiberty/guide-microprofile-rest-client-async/tar.gz/refs/heads/prod","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247697931,"owners_count":20981273,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T05:15:51.186Z","updated_at":"2025-04-07T17:32:37.764Z","avatar_url":"https://github.com/OpenLiberty.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"// Copyright (c) 2019, 2025 IBM Corporation and others.\n// Licensed under Creative Commons Attribution-NoDerivatives\n// 4.0 International (CC BY-ND 4.0)\n//   https://creativecommons.org/licenses/by-nd/4.0/\n//\n// Contributors:\n//   IBM Corporation\n:projectid: microprofile-rest-client-async\n:page-layout: guide-multipane\n:page-duration: 15 minutes\n:page-releasedate: 2019-09-13\n:page-majorupdateddate: 2024-04-04\n:page-guide-category: microprofile\n:page-essential: false\n:page-description: Learn how to use MicroProfile Rest Client to invoke RESTful microservices asynchronously over HTTP.\n:page-seo-title: Consuming RESTful Java microservices asynchronously using Eclipse MicroProfile Rest Client\n:page-seo-description: A getting started tutorial and an example on how to consume RESTful Java microservices with asynchronous method calls using the CompletionStage interface and MicroProfile Rest Client.\n:guide-author: Open Liberty\n:page-tags: ['microprofile']\n:page-permalink: /guides/{projectid}\n:page-related-guides: ['microprofile-reactive-messaging', 'reactive-rest-client']\n:common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod\n:imagesdir: /img/guide/{projectid}\n:source-highlighter: prettify\n:mac: MAC\n:win: WINDOWS\n:linux: LINUX\n= Consuming RESTful services asynchronously with template interfaces\n\n[.hidden]\nNOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].\n\nLearn how to use MicroProfile Rest Client to invoke RESTful microservices asynchronously over HTTP.\n\n== What you'll learn\n\nYou will learn how to build a MicroProfile Rest Client to access remote RESTful services using asynchronous method calls. You'll update the template interface for a MicroProfile Rest Client to use the `CompletionStage` return type. The template interface maps to the remote service that you want to call. A `CompletionStage` interface allows you to work with the result of your remote service call asynchronously.\n\n*What is asynchronous programming?*\n\nImagine asynchronous programming as a restaurant. After you're seated, a waiter takes your order. Then, you must wait a few minutes for your food to be prepared. While your food is being prepared, your waiter may take more orders or serve other tables. After your food is ready, your waiter brings out the food to your table. However, in a synchronous model, the waiter must wait for your food to be prepared before serving any other customers. This method blocks other customers from placing orders or receiving their food.\n\nYou can perform lengthy operations, such as input/output (I/O), without blocking with asynchronous methods. The I/O operation can occur in the background and a callback notifies the caller to continue its computation when the original request is complete. As a result, the original thread frees up so it can handle other work rather than wait for the I/O to complete. Revisiting the restaurant analogy, food is prepared asynchronously in the kitchen and your waiter is freed up to attend to other tables.\n\nIn the context of REST clients, HTTP request calls can be time consuming. The network might be slow, or maybe the upstream service is overwhelmed and can't respond quickly. These lengthy operations can block the execution of your thread when it's in use and prevent other work from being completed.\n\nThe application in this guide consists of three microservices, `system`, `inventory`, and `query`. Every 15 seconds the `system` microservice calculates and publishes an event that contains its average system load. The `inventory` microservice subscribes to that information so that it can keep an updated list of all the systems and their current system loads. \n\nimage::QueryService.png[Reactive Inventory System,width=253,height=327,align=\"center\"]\n\nThe microservice that you will modify is the `query` service. It communicates with the `inventory` service to determine which system has the highest system load and which system has the lowest system load. \n\nThe `system` and `inventory` microservices use MicroProfile Reactive Messaging to send and receive the system load events. If you want to learn more about reactive messaging, see the https://openliberty.io/guides/microprofile-reactive-messaging.html[Creating Reactive Java Microservices^] guide.\n\n// =================================================================================================\n// Prerequisites\n// =================================================================================================\n== Additional prerequisites\n\nBefore you begin, Docker needs to be installed. For installation instructions, refer to the official https://docs.docker.com/get-docker/[Docker documentation^]. You will build and run the microservices in Docker containers. An installation of Apache Kafka is provided in another Docker container.\n\n// =================================================================================================\n// Getting started\n// =================================================================================================\n\n[role='command']\ninclude::{common-includes}/gitclone.adoc[]\n\n== Updating the template interface of a REST client to use asynchronous methods\n\n// static guide instructions:\nifndef::cloud-hosted[]\nNavigate to the `start` directory to begin.\nendif::[]\n\n// cloud-hosted guide instructions:\nifdef::cloud-hosted[]\nTo begin, run the following command to navigate to the ***start*** directory:\n```bash\ncd /home/project/guide-microprofile-rest-client-async/start\n```\nendif::[]\n\nThe `query` service uses a MicroProfile Rest Client to access the `inventory` service. You will update the methods in the template interface for this client to be asynchronous.\n\n[role=\"code_command hotspot\", subs=\"quotes\"]\n----\n#Replace the `InventoryClient` interface.#\n`query/src/main/java/io/openliberty/guides/query/client/InventoryClient.java`\n----\n\nInventoryClient.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/query/src/main/java/io/openliberty/guides/query/client/InventoryClient.java[]\n----\n\nThe changes involve the [hotspot=getSystem file=0]`getSystem` method. Change the return type to `CompletionStage\u003cProperties\u003e` to make the method asynchronous. The method now has the return type of `CompletionStage\u003cProperties\u003e` so you aren't able to directly manipulate the `Properties` inner type. As you will see in the next section, you're able to indirectly use the `Properties` by chaining callbacks.\n\n== Updating a REST resource to asynchronously handle HTTP requests\n\nTo reduce the processing time, you will update the `/query/systemLoad` endpoint to asynchronously send the requests. Multiple client requests will be sent synchronously in a loop. The asynchronous calls do not block the program so the endpoint needs to ensure that all calls are completed and all returned data is processed before proceeding.\n\n[role=\"code_command hotspot\", subs=\"quotes\"]\n----\n#Replace the `QueryResource` class.#\n`query/src/main/java/io/openliberty/guides/query/QueryResource.java`\n----\n\nQueryResource.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/query/src/main/java/io/openliberty/guides/query/QueryResource.java[]\n----\n\nFirst, the [hotspot=systemLoad file=0]`systemLoad` endpoint first gets all the hostnames by calling [hotspot=getSystems file=0]`getSystems()`. In the [hotspot=getSystem file=0]`getSystem()` method, multiple requests are sent asynchronously to the `inventory` service for each hostname. When the requests return, the [hotspot=thenAcceptAsync file=0]`thenAcceptAsync()` method processes the returned data with the `CompletionStage\u003cProperties\u003e` interface.\n\nThe `CompletionStage\u003cProperties\u003e` interface represents a unit of computation. After a computation is complete, it can either be finished or it can be chained with more `CompletionStage\u003cProperties\u003e` interfaces using the [hotspot=thenAcceptAsync file=0]`thenAcceptAsync()` method. Exceptions are handled in a callback that is provided to the [hotspot=exceptionally file=0]`exceptionally()` method, which behaves like a catch block. When you return a `CompletionStage\u003cProperties\u003e` type in the resource, it doesn’t necessarily mean that the computation completed and the response was built. JAX-RS responds to the caller after the computation completes.\n\nIn the [hotspot=systemLoad file=0]`systemLoad()` method a [hotspot=countdown1 hotspot=countdown2 hotspot=countdown3 file=0]`CountDownLatch` object is used to track asynchronous requests. The [hotspot=countdown2 hotspot=countdown3 file=0]`countDown()` method is called whenever a request is complete. When the [hotspot=countdown1 hotspot=countdown2 hotspot=countdown3 file=0]`CountDownLatch` is at zero, it indicates that all asynchronous requests are complete. By using the [hotspot=await file=0]`await()` method of the [hotspot=countdown1 hotspot=countdown2 hotspot=countdown3 file=0]`CountDownLatch`, the program waits for all the asynchronous requests to be complete. When all asynchronous requests are complete, the program resumes execution with all required data processed. \n\nA [hotspot=holder file=0]`Holder` class is used to wrap a variable called `values` that has the [hotspot=volatile file=0]`volatile` keyword. The `values` variable is instantiated as a [hotspot=concurrentHashMap file=0]`ConcurrentHashMap` object. Together, the `volatile` keyword and `ConcurrentHashMap` type allow the `Holder` class to store system information and safely access it asynchronously from multiple threads.\n\n// =================================================================================================\n// Building the application\n// =================================================================================================\n\n== Building and running the application\n\nYou will build and run the `system`, `inventory`, and `query` microservices in Docker containers. You can learn more about containerizing microservices with Docker in the https://openliberty.io/guides/containerize.html[Containerizing microservices^] guide.\n\nStart your Docker environment. Dockerfiles are provided for you to use.\n\nTo build the application, run the Maven `install` and `package` goals from the command-line session in the `start` directory:\n\n[role='command']\n```\nmvn -pl models install\nmvn package\n```\n\n\n\nRun the following commands to containerize the microservices:\n\n[role='command']\n```\ndocker build -t system:1.0-SNAPSHOT system/.\ndocker build -t inventory:1.0-SNAPSHOT inventory/.\ndocker build -t query:1.0-SNAPSHOT query/.\n```\n\nNext, use the provided `startContainers` script to start the application in Docker containers. The script creates containers for Kafka and all of the microservices in the project, in addition to a network for the containers to communicate with each other. The script also creates three instances of the `system` microservice. \n\ninclude::{common-includes}/os-tabs.adoc[]\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\n.\\scripts\\startContainers.bat\n```\n--\n\n[.tab_content.mac_section]\n--\n[role='command']\n```\n./scripts/startContainers.sh\n```\n--\n\n[.tab_content.linux_section]\n--\n[role='command']\n```\n./scripts/startContainers.sh\n```\n--\n\n// static guide instructions:\nifndef::cloud-hosted[]\nThe services take some time to become available. Visit the http://localhost:9085/health[^] URL to confirm that the `inventory` microservice is up and running.\n\nYou can access the application by making requests to the `query/systemLoad` endpoint by going to the http://localhost:9080/query/systemLoad[http://localhost:9080/query/systemLoad^] URL.\nendif::[]\n\n// cloud-hosted guide instructions:\nifdef::cloud-hosted[]\nThe services might take several minutes to become available. Run the following curl command to confirm that the ***inventory*** microservice is up and running.\n```bash\ncurl -s http://localhost:9085/health | jq\n```\n\nYou can access the application by making requests to the ***query/systemLoad*** endpoint by running the following curl command:\n```bash\ncurl -s http://localhost:9080/query/systemLoad | jq\n```\nendif::[]\n\nWhen the service is ready, you see an output similar to the following example which was formatted for readability. \n\n[source, role='no_copy']\n----\n{\n    \"highest\": {\n        \"hostname\" : \"8841bd7d6fcd\",\n        \"systemLoad\" : 6.96\n    },\n    \"lowest\": {\n        \"hostname\" : \"37140ec44c9b\",\n        \"systemLoad\" : 6.4\n    }\n}\n----\n\nSwitching to an asynchronous programming model freed up the thread that handles requests to the `inventory` service. While requests process, the thread can handle other work or requests. In the [hotspot=systemLoad file=0]`/query/systemLoad` endpoint, multiple systems are read and compared at once.\n\nWhen you are done checking out the application, run the following script to stop the application:\n\ninclude::{common-includes}/os-tabs.adoc[]\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\n.\\scripts\\stopContainers.bat\n```\n--\n\n[.tab_content.mac_section]\n--\n[role='command']\n```\n./scripts/stopContainers.sh\n```\n--\n\n[.tab_content.linux_section]\n--\n[role='command']\n```\n./scripts/stopContainers.sh\n```\n--\n\n// =================================================================================================\n// Testing\n// =================================================================================================\n\n== Testing the query microservice\n\nYou will create an endpoint test to test the basic functionality of the `query` microservice. If a test failure occurs, then you might have introduced a bug into the code.\n\n[role=\"code_command hotspot\", subs=\"quotes\"]\n----\n#Create the `QueryServiceIT` class.#\n`query/src/test/java/it/io/openliberty/guides/query/QueryServiceIT.java`\n----\n\nThe [hotspot=testLoads file=0]`testLoads()` test case verifies that the `query` service can calculate the highest and lowest system loads. \n\nQueryServiceIT.java\n[source, java, linenums, role='code_column hide_tags=copyright,javadoc']\n----\ninclude::finish/query/src/test/java/it/io/openliberty/guides/query/QueryServiceIT.java[]\n----\n\n// =================================================================================================\n// Running the tests\n// =================================================================================================\n\n=== Running the tests\n\n// static guide instructions:\nifndef::cloud-hosted[]\nNavigate to the `query` directory, then verify that the tests pass by using the Maven `verify` goal:\n\ninclude::{common-includes}/os-tabs.adoc[]\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmvn verify\n```\n--\n\n[.tab_content.mac_section]\n--\n[role='command']\n```\nexport TESTCONTAINERS_RYUK_DISABLED=true\nmvn verify\n```\n\nFor more information about disabling Ryuk, see the https://java.testcontainers.org/features/configuration/#disabling-ryuk[Testcontainers custom configuration^] document.\n--\n\n[.tab_content.linux_section]\n--\n[role='command']\n```\nexport TESTCONTAINERS_RYUK_DISABLED=true\nmvn verify\n```\n\nFor more information about disabling Ryuk, see the https://java.testcontainers.org/features/configuration/#disabling-ryuk[Testcontainers custom configuration^] document.\n--\n\nWhen the tests succeed, you see output similar to the following example:\nendif::[]\n\n// cloud-hosted guide instructions:\nifdef::cloud-hosted[]\nRun the following commands to navigate to the ***query*** directory and verify that the tests pass by using the Maven ***verify*** goal:\n```bash\nexport TESTCONTAINERS_RYUK_DISABLED=true\ncd /home/project/guide-microprofile-rest-client-async/start/query\nmvn verify\n```\n\nFor more information about disabling Ryuk, see the [Testcontainers custom configuratio](https://java.testcontainers.org/features/configuration/#disabling-ryuk) document.\n\nThe tests might take a few minutes to complete. When the tests succeed, you see output similar to the following example:\nendif::[]\n\n[source, role='no_copy']\n----\n-------------------------------------------------------\n T E S T S\n-------------------------------------------------------\nRunning it.io.openliberty.guides.query.QueryServiceIT\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 32.123 s - in it.io.openliberty.guides.query.QueryServiceIT\n\nResults:\n\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0\n----\n\n== Great work! You're done!\n\nYou have just modified an application to make asynchronous HTTP requests using Open Liberty and MicroProfile Rest Client.\n\ninclude::{common-includes}/attribution.adoc[subs=\"attributes\"]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenliberty%2Fguide-microprofile-rest-client-async","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenliberty%2Fguide-microprofile-rest-client-async","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenliberty%2Fguide-microprofile-rest-client-async/lists"}