- What you’ll learn
- Getting started
- Updating the template interface of a REST client to use asynchronous methods
- Updating a REST resource to asynchronously handle HTTP requests
- Building and running the application
- Testing the gateway application
- Tearing down the environment
- Great work! You’re done!
- Guide Attribution
Consuming RESTful services asynchronously with template interfaces
Learn how to use MicroProfile Rest Client to invoke RESTful microservices asynchronously over HTTP.
What you’ll learn
You 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, which maps to the remote service that you want to call, to use the
CompletionStage return type. A
CompletionStage interface allows you to work with the result of your remote service call asynchronously.
What is asynchronous programming?
Imagine 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.
You 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.
In 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.
The application that you’ll be working with is a job manager that maintains an inventory of available systems. It consists of four microservices,
job microservice allows you to dispatch jobs that will be run by the
system microservice. A job is a sleep operation that is used to represent a slow task that lasts for a duration of 5 to 10 seconds. When a job completes, the
system microservice reports the sleep time that results from the job. In addition to running jobs, the
system microservice also registers itself at startup with the
inventory microservice, which keeps track of all instances of the
system microservice. Finally, the
gateway microservice is a backend for frontend service. It communicates with the backend
inventory microservices on the caller’s behalf.
The microservice that you will modify is the
gateway service. It acts as a gateway to communicate with the backend microservices. Whenever a request is made to the
gateway service to retrieve the jobs, the
gateway service communicates with the
job service on that host to get the completed jobs.
The implementations of the application and its services are provided for you in the
start/src directory. The application also uses the Apache Kafka and ZooKeeper services to distribute the job results and system status.
If you want to learn more about MicroProfile Rest Client, you can read the Consuming RESTful services with template interfaces guide.
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
git clone https://github.com/openliberty/guide-microprofile-rest-client-async.git cd guide-microprofile-rest-client-async
start directory contains the starting project that you will build upon.
finish directory contains the finished project that you will build.
Updating the template interface of a REST client to use asynchronous methods
Navigate to the
start directory to begin.
gateway service uses a MicroProfile Rest Client to access the
job service. You will update the methods in the template interface for this client to be asynchronous.
The changes involve changing the return types of the
createJob methods to return
CompletionStage<T> types. These changes make the methods asynchronous. Since the methods now have return type of
CompletionStage<T>, you aren’t able to directly manipulate the inner types. As you will see in the next section, you will be able to indirectly use the inner object by chaining callbacks.
Updating a REST resource to asynchronously handle HTTP requests
JAX-RS resources can also have asynchronous methods. So instead of returning a
JobsList model type, you can return a
CompletionStage<JobsList> type. Completion stages can be chained together by using the
Similar to the synchronous approach, if completed jobs are successfully obtained from the
job microservice, the resource responds with an HTTP status of 200 and the body will contain a list of jobs. Finally, return the
CompletionStage<JobsList> result you built by using the
CompletionStage interface represents a unit of computation. After that computation completes, it can either be finished or chained with more completion stages by using the
thenApplyAsync() method to perform more computations. Exceptions can be handled in a callback provided to the
exceptionally() method, which behaves similar to a catch block. When you return a
CompletionStage type in the resource, it doesn’t necessarily mean that the computation completed and the response was built. JAX-RS will respond to the caller after the
Building and running the application
You will build and run the
inventory microservices in Docker containers. You can learn more about containerizing microservices with Docker in the Containerizing microservices guide.
Install Docker by following the instructions on the official Docker documentation. Start your Docker environment.
To build the application, run the Maven
install goal from the command line in the
mvn clean install
Run the following command to download or update to the latest
openliberty/open-liberty:kernel-java8-openj9-ubi Docker image:
docker pull openliberty/open-liberty:kernel-java8-openj9-ubi
Run the following commands to build and containerize the application:
docker build -t system:1.0-SNAPSHOT system/. docker build -t inventory:1.0-SNAPSHOT inventory/. docker build -t job:1.0-SNAPSHOT job/. docker build -t gateway:1.0-SNAPSHOT gateway/.
Next, use the provided script to start the application in Docker containers. The script creates a network for the containers to communicate with each other. It also creates containers for Kafka, Zookeeper, and all of the microservices in the project.
The services take some time to become available. You can access the application by making requests to the
gateway job endpoints:
Get completed jobs
Create a job
Get a specific job
To create a job, you can use
curl -X POST http://localhost:8080/api/jobs command if available on the system. The Postman application can also be used. The request take some time for the job results to return.
The completed jobs JSON output with a created job looks like
averageResult attribute is the average sleep time of all the jobs. The
count attribute is the number of jobs, and the
results attribute contains the list of the jobs. The JSON output for each job has a job ID and a sleep time as the result for the job.
If no jobs are created, the JSON output will be
count attribute is 0 and the
results attribute is empty.
Switching to an asynchronous programming model freed up the thread that was handling your request to
/api/jobs. While the request is processed, the thread can handle other work.
Testing the gateway application
You will create an endpoint test to test the basic functionality of the
gateway microservice. If a test failure occurs, then you may have introduced a bug into the code.
The following descriptions explain what the test cases verify:
testCreateJob()test case verifies that the
gatewayservice communicates with a backend service to create a job.
testGetJobs()test case verifies that the
gatewayservice communicates with an upstream service to get all jobs and transforms them to a
Running the tests
Navigate to the
gateway directory, then verify that the tests pass by using the Maven
When the tests succeed, you see output similar to the following example:
------------------------------------------------------- T E S T S ------------------------------------------------------- Running it.io.openliberty.guides.gateway.GatewayInventoryEndpointIT Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.88 s - in it.io.openliberty.guides.gateway.GatewayInventoryEndpointIT Running it.io.openliberty.guides.gateway.GatewayJobEndpointIT Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.455 s - in it.io.openliberty.guides.gateway.GatewayJobEndpointIT Results: Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
Tearing down the environment
Navigate back to the
Finally, use the following script to stop the application:
Great work! You’re done!
You have just modified an application to make asynchronous HTTP requests using Open Liberty and MicroProfile Rest Client.
Consuming RESTful services asynchronously with template interfaces by Open Liberty is licensed under CC BY-ND 4.0
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
Thank you for your feedback!
Would you like to open an issue in GitHub?Yes
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide