Http async request java

Asynchronous HTTP with async-http-client in Java

If you have a few years of experience in the Java ecosystem, and you’re interested in sharing that experience with the community (and getting paid for your work of course), have a look at the «Write for Us» page. Cheers, Eugen

1. Overview

AsyncHttpClient (AHC) is a library build on top of Netty, with the purpose of easily executing HTTP requests and processing responses asynchronously.

In this article, we’ll present how to configure and use the HTTP client, how to execute a request and process the response using AHC.

2. Setup

The latest version of the library can be found in the Maven repository. We should be careful to use the dependency with the group id org.asynchttpclient and not the one with com.ning:

 org.asynchttpclient async-http-client 2.2.0 

3. HTTP Client Configuration

The most straightforward method of obtaining the HTTP client is by using the Dsl class. The static asyncHttpClient() method returns an AsyncHttpClient object:

AsyncHttpClient client = Dsl.asyncHttpClient();

If we need a custom configuration of the HTTP client, we can build the AsyncHttpClient object using the builder DefaultAsyncHttpClientConfig.Builder:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

This offers the possibility to configure timeouts, a proxy server, HTTP certificates and many more:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config() .setConnectTimeout(500) .setProxyServer(new ProxyServer(. )); AsyncHttpClient client = Dsl.asyncHttpClient(clientBuilder);

Once we’ve configured and obtained an instance of the HTTP client we can reuse it across out application. We don’t need to create an instance for each request because internally it creates new threads and connection pools, which will lead to performance issues.

Читайте также:  Javascript textarea в строку

Also, it’s important to note that once we’ve finished using the client we should call to close() method to prevent any memory leaks or hanging resources.

4. Creating an HTTP Request

There are two methods in which we can define an HTTP request using AHC:

There is no major difference between the two request types in terms of performance. They only represent two separate APIs we can use to define a request. A bound request is tied to the HTTP client it was created from and will, by default, use the configuration of that specific client if not specified otherwise.

For example, when creating a bound request the disableUrlEncoding flag is read from the HTTP client configuration, while for an unbound request this is, by default set to false. This is useful because the client configuration can be changed without recompiling the whole application by using system properties passed as VM arguments:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

A complete list of properties can be found the ahc-default.properties file.

4.1. Bound Request

To create a bound request we use the helper methods from the class AsyncHttpClient that start with the prefix “prepare”. Also, we can use the prepareRequest() method which receives an already created Request object.

For example, the prepareGet() method will create an HTTP GET request:

BoundRequestBuilder getRequest = client.prepareGet("http://www.baeldung.com");

4.2. Unbound Request

An unbound request can be created using the RequestBuilder class:

Request getRequest = new RequestBuilder(HttpConstants.Methods.GET) .setUrl("http://www.baeldung.com") .build();

or by using the Dsl helper class, which actually uses the RequestBuilder for configuring the HTTP method and URL of the request:

Request getRequest = Dsl.get("http://www.baeldung.com").build()

5. Executing HTTP Requests

The name of the library gives us a hint about how the requests can be executed. AHC has support for both synchronous and asynchronous requests.

Executing the request depends on its type. When using a bound request we use the execute() method from the BoundRequestBuilder class and when we have an unbound request we’ll execute it using one of the implementations of the executeRequest() method from the AsyncHttpClient interface.

5.1. Synchronously

The library was designed to be asynchronous, but when needed we can simulate synchronous calls by blocking on the Future object. Both execute() and executeRequest() methods return a ListenableFuture object. This class extends the Java Future interface, thus inheriting the get() method, which can be used to block the current thread until the HTTP request is completed and returns a response:

Future responseFuture = boundGetRequest.execute(); responseFuture.get();
Future responseFuture = client.executeRequest(unboundRequest); responseFuture.get();

Using synchronous calls is useful when trying to debug parts of our code, but it’s not recommended to be used in a production environment where asynchronous executions lead to better performance and throughput.

5.2. Asynchronously

When we talk about asynchronous executions, we also talk about listeners for processing the results. The AHC library provides 3 types of listeners that can be used for asynchronous HTTP calls:

The AsyncHandler listener offers the possibility to control and process the HTTP call before it has completed. Using it can handle a series of events related to the HTTP call:

request.execute(new AsyncHandler() < @Override public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception < return null; >@Override public State onHeadersReceived(HttpHeaders headers) throws Exception < return null; >@Override public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception < return null; >@Override public void onThrowable(Throwable t) < >@Override public Object onCompleted() throws Exception < return null; >>);

The State enum lets us control the processing of the HTTP request. By returning State.ABORT we can stop the processing at a specific moment and by using State.CONTINUE we let the processing finish.

It’s important to mention that the AsyncHandler isn’t thread-safe and shouldn’t be reused when executing concurrent requests.

AsyncCompletionHandler inherits all the methods from the AsyncHandler interface and adds the onCompleted(Response) helper method for handling the call completion. All the other listener methods are overridden to return State.CONTINUE, thus making the code more readable:

request.execute(new AsyncCompletionHandler() < @Override public Object onCompleted(Response response) throws Exception < return response; >>);

The ListenableFuture interface lets us add listeners that will run when the HTTP call is completed.

Also, it let’s execute the code from the listeners – by using another thread pool:

ListenableFuture listenableFuture = client .executeRequest(unboundRequest); listenableFuture.addListener(() -> < Response response = listenableFuture.get(); LOG.debug(response.getStatusCode()); >, Executors.newCachedThreadPool());

Besides, the option to add listeners, the ListenableFuture interface lets us transform the Future response to a CompletableFuture.

7. Conclusion

AHC is a very powerful library, with a lot of interesting features. It offers a very simple way to configure an HTTP client and the capability of executing both synchronous and asynchronous requests.

As always, the source code for the article is available over on GitHub.

Источник

Asynchronous-IO vs Asynchronous-Request Processing in java

Bhuvan Gupta

Bhuvan Gupta

Asynchronous-IO vs Asynchronous-Request Processing in java

In this article, I am trying to explain the difference between Async-IO and Async-Request processing in the HTTP request in the Java world.

In the pre-Java 1.4 world, Java provides an API to send/receive data over the network socket. The original authors of JVM mapped this API behavior to OS socket API, almost one to one.

So, what is the OS socket behaviour? OS provides Socket programming api, which has send/recv blocking call. Since java is just a process running on top of linux(OS), hence this java program has to use this blocking api provided by OS.

The world was happy and java developers started using the API to send/receive the data. But they had to keep one java thread for every socket(client).

Everybody was writing their own flavor of HTTP servers. Code sharing was becoming hard, the java world demanded a standardization.
Enters the java servlet Spec.

Before moving on lets define few terms:

Java Server Developer: People who are using the java socket api and implementing http protocol like tomcat.

java Application Developer: People who are building buisness application on top of tomcat.

Once the java servlet spec entered the world, it said:

Dear java server developers, please provide a method like below:

so that java application developer can implement doGet and they can write their business logic. Once “application developer” wants to send the response , he can call OutPutRes.write().

A thing to Note:Since socket api is blocking, hence OutPutRes.write() is also blocking. Also, the additional limitation was that the response object is committed on doGet method exit.

Due to these limitations, people had to use one thread for processing one request.

Time passed and the internet took over the world. one Thread per Request started to show limitations.

Problem 1:

The thread-per-request model fails when there are long pauses during the processing of each request.

Under such a situation, the thread is mostly sitting idle and JVM can run out of thread easily.

Problem 2:

Things got even worse with http1.1 persistent connection. As with persistent connection, the underlying TCP connection will be kept alive and the server has to block one thread per connection.

But why does the server have to block one thread per connection?
Since OS provides a blocking socket Recv api, the jvm has to call the OS blocking Recv method in order to listen for more requests on same tcp connection from the client.

The world demanded a solution!

The First Solution came from the creator of JVM. They introduced NIO(ASYNC-IO). Nio is the non-blocking API for sending/receiving data over socket.

Some background: the OS along with blocking socket api also provides a non-blocking version of the socket api.

But how does the OS provide that .. Does it fork a thread internally and that thread gets blocked.

The ANSWER is no… the OS instruct the hardware to interupt when there is data to read or write.

NIO allowed the java server developer” to tackle problem 2 of blocking one thread per TCP connection. With NIO being an HTTP persistent connection, the thread does not require it to block on recv call. Instead, it can now process it only when there is data to be processed. This allowed one thread to monitor/handle a large number of persistent connections.

The Second Solution came from servlet spec. Servlet Spec got an upgrade and they introduced async support (Async Request Processing).

AsyncContext acontext = req.startAsync();

IMPORTANT: This upgrade removed the limitation of committing the response object on doGet method completion.

This allowed the “Java Application Developer” to tackle Problem 1, by offloading work to background threads. Now instead of keeping the thread waiting during the long pause, the thread can be used to handle other requests.

CONCLUSION:

Async-IO in java is basically using the non-blocking version on OS socket API.

Async request processing is basically the servlet spec standardization of how to process more requests with one thread.

Источник

Оцените статью