Sunday, September 17, 2023

How to Send Asynchronous HTTP Requests using HttpClient in Java? Example Tutorial

ending HTTP requests is a common operation in modern web development for dealing with various APIs, online services, and servers. HTTP requests are often delivered synchronously, which means the programme waits for the response before moving on. However, asynchronous HTTP queries are helpful in situations when responsiveness and performance are essential. Asynchronous requests enable the programme to carry on running even as it waits in the background for the server to respond. The process of sending asynchronous HTTP requests in Java using HttpClient will be covered in this article, along with sample code, pros, cons, and some key points.

But, before we dive into the how can we send asynchronous request using HttpClient in Java let’s first understand the key terms.


What is HttpClient in Java?

HttpClient is a part of the Java Standard Library that provides an efficient way to send HTTP requests and receive responses from servers. It simplifies the process of making HTTP requests and handling responses, making it a popular choice for Java developers.


Sending Synchronous HTTP Requests

Before diving into asynchronous requests, let's first understand how synchronous requests are typically made using HttpClient. The following code snippet demonstrates how to send a synchronous HTTP GET request:


import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.io.IOException;


public class SynchronousHttpClientExample {

    public static void main(String[] args) throws IOException, 
       InterruptedException, URISyntaxException {

        HttpClient httpClient = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("https://api.example.com/data"))
                .GET()
                .build();

        HttpResponse<String> response 
          = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println("Response: " + response.body());

    }

}


Introducing Asynchronous HTTP Requests

To send asynchronous HTTP requests, Java provides CompletableFuture, which is a part of the Java Concurrency API introduced in Java 8. By leveraging CompletableFuture, we can achieve asynchronous behavior with HttpClient. The following code demonstrates how to send an asynchronous HTTP GET request:

import java.net.http.HttpClient;

import java.net.http.HttpRequest;

import java.net.http.HttpResponse;

import java.net.URI;

import java.net.URISyntaxException;

import java.util.concurrent.CompletableFuture;



public class AsynchronousHttpClientExample {

    public static void main(String[] args) throws URISyntaxException {

        HttpClient httpClient = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()

                .uri(new URI("https://api.example.com/data"))

                .GET()

                .build();



        CompletableFuture<HttpResponse<String>> responseFuture =

                httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString());



        responseFuture.thenAccept(response -> {

            System.out.println("Response: " + response.body());

        });



        // Other tasks can be performed here while waiting for the response.

        // ...



        // Ensure the application doesn't exit immediately,
        // giving the asynchronous request a chance to complete.

        responseFuture.join();

    }

}


In this example, `httpClient.sendAsync()` returns a CompletableFuture that represents the future result of the HTTP request. We then use `thenAccept()` to specify a callback to handle the response when it becomes available. The `responseFuture.join()` ensures that the application waits for the completion of the HTTP request before exiting.


Let’s take another example to better grasp the HTTP Request

import java.net.URI;

import java.net.URISyntaxException;

import java.net.http.HttpClient;

import java.net.http.HttpRequest;

import java.net.http.HttpResponse;

import java.util.Arrays;

import java.util.List;

import java.util.concurrent.CompletableFuture;

import java.util.stream.Collectors;



public class AsyncWeatherApiClient {

    public static void main(String[] args) {

        List<String> cities = Arrays.asList("London", "New York", 
           "Sydney", "Tokyo", "Paris");



        // Send asynchronous requests and collect the results using CompletableFuture.allOf

        CompletableFuture<Void> allRequests = CompletableFuture.allOf(

                cities.stream()

                        .map(city -> getWeatherForCity(city))

                        .toArray(CompletableFuture[]::new)

        );



        // Wait for all requests to complete

        allRequests.join();

    }



    private static CompletableFuture<Void> getWeatherForCity(String city) {

        HttpClient httpClient = HttpClient.newHttpClient();

        HttpRequest request;

        try {

            request = HttpRequest.newBuilder()

                    .uri(new URI("https://api.openweathermap.org/data/2.5/weather?q=" 
 + city + 

"&appid=YOUR_API_KEY"))

                    .GET()

                    .build();

        } catch (URISyntaxException e) {

            System.err.println("Invalid URI: " + e.getMessage());

            return CompletableFuture.completedFuture(null);

        }



        return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())

                .thenApply(response -> {

                    if (response.statusCode() == 200) {

                        System.out.println("City: " + city + ", Weather: " 
   + response.body());

                    } else {

                        System.out.println("Error fetching weather data for " 
    + city + ", Status code: " + 		response.statusCode());

                    }

                    return null;

                })

                .exceptionally(e -> {

                    System.err.println("Error fetching weather data for " 
                 + city + ": " + e.getMessage());

                    return null;

                });

    }

}


Program written above define a list of cities for which we want to fetch weather data. We then send asynchronous HTTP GET requests to the API for each city using CompletableFuture and HttpClient. The getWeatherForCity() method sends a single asynchronous request for a given city and handles the response or any exceptions that may occur.

The CompletableFuture.allOf method is used to wait for all asynchronous requests to complete before the program exits. This method takes an array of CompletableFutures and returns a new CompletableFuture that completes when all the provided CompletableFutures are completed.

When you run this program, you will notice that it fetches weather data for all cities concurrently, and the responses are printed as they arrive. This demonstrates the non-blocking nature of asynchronous requests, which allows the program to proceed with other tasks while waiting for responses from the API.


Pros of Asynchronous HTTP Requests

Asynchronous HTTP requests offer several advantages over their synchronous counterparts:

1. Improved Performance

Asynchronous requests allow the program to continue executing other tasks while waiting for the server's response. This can lead to better overall performance and responsiveness, especially in applications that need to handle many concurrent requests.

2. Reduced Blocking

In synchronous requests, the program blocks and waits for the server's response, which can lead to idle periods. Asynchronous requests help eliminate this blocking behavior, leading to more efficient resource utilization.

3. Concurrency Handling

Asynchronous requests facilitate easier handling of concurrent requests. Modern applications often deal with numerous incoming requests, and asynchronous processing can efficiently manage these requests without creating bottlenecks.

4. Scalability

Asynchronous communication allows systems to scale better by efficiently utilizing resources and handling more requests without a significant increase in resource consumption.


How to Send Asynchronous HTTP Requests Using HttpClient in Java



Cons of Asynchronous HTTP Requests

While asynchronous HTTP requests offer several benefits, they also come with some challenges and potential drawbacks:

1. Complexity

Asynchronous programming introduces complexity compared to synchronous programming. Managing callbacks, error handling, and ensuring data consistency can be more challenging in asynchronous scenarios.

2. Error Handling

Error handling can be more complex with asynchronous requests, as exceptions might not be thrown immediately, and errors may need to be handled in callback functions.

3. Debugging

Debugging asynchronous code can be trickier due to the non-linear flow of execution. It can be challenging to trace the sequence of events and identify issues.


Things to Remember when using Asynchronous HttpClient

Here are few things you should remember while doing asynchronous communication over HTTP in Java.

1. Error Handling

You should implement robust error handling mechanisms in your asynchronous callbacks. Consider using the `exceptionally()` method of CompletableFuture to handle exceptions.

2. Rate Limiting

Be mindful of sending too many asynchronous requests simultaneously, which could lead to server overload or rate limiting from APIs.


Conclusion

That's all about how to send asynchronous HTTP request using HttpClient in Java. Using Java's HttpClient to do asynchronous HTTP queries has many advantages, including better performance, less blocking, and more scalability. Developers can create applications that are more responsive and effective by utilising CompletableFuture and asynchronous programming.

Asynchronous programming comes with additional complexity and difficulties, thus it's important to grasp them and utilise it sparingly depending on the demands of your application. 

You may take use of the benefits of asynchronous HTTP requests and create reliable, high-performing Java applications by keeping in mind the important ideas presented in this article.


Now, over to you? what is your favorite HTTP library in Java? HttpClient of JDK 11, Apache HttpClient library or HttpURLConnection of JDK 1,0?

No comments:

Post a Comment

Feel free to comment, ask questions if you have any doubt.