Open-source project
AsyncHttpClient/async-http-client avatar
AsyncHttpClient/async-http-client

AsyncHttpClient: a Netty-based HTTP and WebSocket client for the JVM

Asynchronous, non-blocking HTTP & WebSocket client for the JVM

6,398 stars1,594 forksJavaApache-2.0

At a glance

What is it?
AsyncHttpClient wraps Netty in a fluent, future-based API for HTTP/1.1, HTTP/2 and WebSocket work. It is a good fit for services that fan out many concurrent calls, and a poor fit for anyone who wants a blocking client.
Who is it for?
Adopt AsyncHttpClient if you run Java 11+ services that issue many concurrent outbound HTTP calls and you are willing to manage a single long-lived client with explicit timeouts and pool limits. Do not adopt it if your code is fundamentally blocking, or if you only need one request per process, because the client is designed to be shared and closing it per request defeats the connection pool.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What AsyncHttpClient solves, and who it is for

The README describes AsyncHttpClient (AHC) as a high-performance, asynchronous HTTP client for Java built on top of Netty, supporting HTTP/1.1, HTTP/2 and WebSocket. The problem it addresses is the thread-per-request model: a service that calls ten downstream APIs with a blocking client needs ten threads parked on sockets. AHC returns a future instead, so the calling thread is released while the socket is in flight. The audience is Java server-side engineers who already run Netty somewhere in their stack, or who need HTTP/2 multiplexing or WebSocket frames without pulling in a second networking library. It is not aimed at scripts, CLI tools or one-off requests; the README explicitly warns that creating a new client per request degrades performance through repeated thread pool and connection pool creation. The project requires Java 11 or later, so anyone still on Java 8 is on the 2.x line, which is still published (2.16.1 appears in the release list).

How the Netty pipeline and the future API fit together

The mechanism is a Netty client under a fluent facade. Requests are built either bound to a client (client.prepareGet(...)) or standalone through the DSL (get(...).build()), then handed to execute(), which returns a ListenableFuture. That future can be converted with toCompletableFuture() for composition, or given a listener that fires on completion. The documentation is direct about the thread model: if the executor passed to addListener is null, the callback runs on the Netty I/O thread, and the README says never to block inside I/O thread callbacks. That single constraint drives most of the design decisions a user has to make. Connection pooling and keep-alive sit underneath, with setMaxConnections and setMaxConnectionsPerHost capping the pool. HTTP/2 is enabled by default over TLS via ALPN, with connection multiplexing and GOAWAY handling, which means a single connection can carry many concurrent streams. Compression is handled automatically for gzip, deflate, Brotli and Zstd, though the README notes Brotli and Zstd need extra dependencies.

Installing AsyncHttpClient from Maven Central

The artifact is org.asynchttpclient:async-http-client. The README shows version 3.0.12 in its examples; the release list also carries 3.0.13, so check Central for the version you want rather than copying the README verbatim. Add the dependency to your build file:

xml
<dependency>
    <groupId>org.asynchttpclient</groupId>
    <artifactId>async-http-client</artifactId>
    <version>3.0.12</version>
</dependency>

Gradle users get the same coordinates in shorthand:

groovy
implementation 'org.asynchttpclient:async-http-client:3.0.12'

With the dependency in place, the smallest useful program creates a client inside a try-with-resources block, prepares a GET, and reads the body off the future. The README's quick start uses exactly this shape, and it is worth copying because it enforces the close() call:

java
import static org.asynchttpclient.Dsl.*;

try (AsyncHttpClient client = asyncHttpClient()) {
    client.prepareGet("https://www.example.com/")
        .execute()
        .toCompletableFuture()
        .thenApply(Response::getResponseBody)
        .thenAccept(System.out::println)
        .join();
}

You should see the response body printed and no thread left hanging. The join() is there only to keep the example short; in production you would return the future or attach a listener with a real executor. If you want the pool and timeout behaviour to be predictable, build the client from config() instead of the no-argument factory:

java
AsyncHttpClient client = asyncHttpClient(config()
    .setConnectTimeout(Duration.ofSeconds(5))
    .setRequestTimeout(Duration.ofSeconds(30))
    .setMaxConnections(500)
    .setMaxConnectionsPerHost(100)
    .setFollowRedirect(true)
    .setMaxRedirects(5)
    .setCompressionEnforced(true));

Those keys are the ones the README documents. Note that setRequestTimeout and setConnectTimeout are separate limits; a slow handshake and a slow response fail differently, and only one of them is covered by the connect timeout.

Where AsyncHttpClient is the wrong tool

The clearest failure mode is blocking in the wrong place. The README warns that a null executor makes the callback run on the Netty I/O thread and that you must never block there. A callback that does a synchronous database call, a file read or a nested blocking HTTP request will stall the event loop that serves other connections on the same thread. The symptom is not an exception; it is latency that spreads across unrelated requests. The second limitation is client lifetime. Because the client owns a thread pool and a connection pool, the README states that instances are long-lived, shared resources and that creating one per request degrades performance. Code that constructs a client inside a request handler, or that closes it after each call, throws away keep-alive and pays pool setup repeatedly. Third, the blocking form is a trap: the README calls execute().get() useful for debugging but notes it defeats the purpose of an async client in production. If your application is genuinely synchronous end to end, a blocking client is simpler and AHC buys you nothing. Finally, the Java 11 floor is a real boundary; projects pinned to Java 8 cannot use the 3.x line.

How it differs from the JDK HttpClient

The JDK ships java.net.http.HttpClient from Java 11 onward, which is the obvious alternative and needs no dependency at all. The difference in approach is breadth rather than the async model, since both return futures. The JDK client covers HTTP/1.1 and HTTP/2 with a CompletableFuture API, but it does not ship a WebSocket frame API in the same shape, a SOCKS proxy implementation, a cookie store, or a filter chain for intercepting requests and responses. AHC documents all of those: HTTP, SOCKS4 and SOCKS5 proxies with CONNECT tunneling, an RFC 6265 cookie store, request and response filters, Basic, Digest, NTLM, SPNEGO/Kerberos and SCRAM-SHA-256 authentication, multipart parts (FilePart, ByteArrayPart, InputStreamPart, StringPart), and a ResumableIOExceptionFilter for resumable downloads. It also exposes optional native transports (Epoll, KQueue, io_uring) that the JDK client does not. The trade-off runs the other way too: AHC pulls in Netty and its transitive dependencies, so a small service that only needs one GET is carrying a networking stack it will not use. The JDK client also has no equivalent of the DSL's bound and unbound request builders, which some teams find convenient for building requests in one layer and executing them in another.

Maintenance, versions and the Apache-2.0 licence

The repository is not archived, and the last push was on 2026-09-21, so the project is being touched. The release list shows 3.0.13 and 3.0.12 published on 2026-08-09, plus 2.16.1 on the same day, which tells you the 2.x branch still receives releases alongside 3.x. That matters for upgrade planning: the README's requirements section says Java 11+, and the 2.x line exists for older runtimes, so a team on Java 8 has a maintained option but not the 3.x feature set. The README does not document a migration path between 2.x and 3.x, so treat the version jump as something to verify against your own code rather than something the documentation will walk you through. On licensing, the project is Apache-2.0, which permits commercial use and modification; the repository carries both a LICENSE file and a LICENSES/ directory, which usually indicates bundled third-party notices. If you redistribute AHC inside a product, read those files rather than assuming the top-level licence covers every dependency. Optional add-ons such as brotli4j and zstd-jni are separate artifacts with their own licences, and native transport classifiers pull in Netty native binaries, so a dependency audit should include them. This is not legal advice; check with counsel if redistribution is part of your plan.

Editorial conclusion

Adopt AsyncHttpClient if you run Java 11+ services that issue many concurrent outbound HTTP calls and you are willing to manage a single long-lived client with explicit timeouts and pool limits. Do not adopt it if your code is fundamentally blocking, or if you only need one request per process, because the client is designed to be shared and closing it per request defeats the connection pool. Before committing, verify three things against your own build: that the 3.x artifact resolves from Maven Central, that your HTTP/2 target works over ALPN, and that no callback you register on a ListenableFuture blocks the Netty I/O thread.

Frequently asked questions

What is AsyncHttpClient?

AsyncHttpClient is an asynchronous, non-blocking HTTP and WebSocket client for the JVM, built on top of Netty. It supports HTTP/1.1, HTTP/2 and WebSocket, and requires Java 11 or later.

How do I add AsyncHttpClient to a Maven project?

Add the dependency org.asynchttpclient:async-http-client to your pom.xml. The README shows version 3.0.12 in its example, and the artifact is published on Maven Central.

What is the difference between a synchronous and an asynchronous HTTP client?

A blocking call parks a thread on the socket until the response arrives, while AsyncHttpClient returns a ListenableFuture that can be converted to a CompletableFuture, so the calling thread is free while the request is in flight. The README notes that the blocking form execute().get() is useful for debugging but defeats the purpose of an async client in production.

Official sources

  1. AsyncHttpClient/async-http-client on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/asynchttpclient-async-http-client.svg)](https://hysenlabs.com/projects/asynchttpclient-async-http-client)