HTTP asinkron dengan async-http-client di Java

1. Ikhtisar

AsyncHttpClient (AHC) adalah pustaka yang dibangun di atas Netty, dengan tujuan mengeksekusi permintaan HTTP dengan mudah dan memproses tanggapan secara asinkron.

Pada artikel ini, kami akan menyajikan cara mengkonfigurasi dan menggunakan klien HTTP, cara menjalankan permintaan, dan memproses respons menggunakan AHC.

2. Penyiapan

Versi terbaru pustaka dapat ditemukan di repositori Maven. Kita harus berhati-hati menggunakan ketergantungan dengan id grup org.asynchttpclient dan bukan yang dengan com.ning:

 org.asynchttpclient async-http-client 2.2.0 

3. Konfigurasi Klien HTTP

Metode paling mudah untuk mendapatkan klien HTTP adalah dengan menggunakan kelas Dsl . Metode asyncHttpClient () statis mengembalikan objek AsyncHttpClient :

AsyncHttpClient client = Dsl.asyncHttpClient();

Jika kita memerlukan konfigurasi kustom untuk klien HTTP, kita bisa membangun objek AsyncHttpClient menggunakan default builder DefaultAsyncHttpClientConfig.Builder :

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

Ini menawarkan kemungkinan untuk mengonfigurasi waktu tunggu, server proxy, sertifikat HTTP, dan banyak lagi:

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

Setelah kami mengonfigurasi dan mendapatkan instance klien HTTP, kami dapat menggunakannya kembali di seluruh aplikasi . Kita tidak perlu membuat instance untuk setiap permintaan karena secara internal itu membuat utas baru dan kumpulan koneksi, yang akan menyebabkan masalah kinerja.

Juga, penting untuk dicatat bahwa setelah kita selesai menggunakan klien, kita harus memanggil metode close () untuk mencegah kebocoran memori atau sumber daya yang menggantung.

4. Membuat Permintaan HTTP

Ada dua metode di mana kita dapat mendefinisikan permintaan HTTP menggunakan AHC:

  • terikat
  • tidak terikat

Tidak ada perbedaan besar antara kedua jenis permintaan dalam hal kinerja. Mereka hanya mewakili dua API terpisah yang dapat kita gunakan untuk menentukan permintaan. Permintaan terikat terikat ke klien HTTP tempat pembuatannya dan akan, secara default, menggunakan konfigurasi klien khusus tersebut jika tidak ditentukan sebaliknya.

Misalnya, saat membuat permintaan terikat, tanda disableUrlEncoding dibaca dari konfigurasi klien HTTP, sedangkan untuk permintaan tidak terikat, secara default disetel ke false. Ini berguna karena konfigurasi klien dapat diubah tanpa mengkompilasi ulang seluruh aplikasi dengan menggunakan properti sistem yang diteruskan sebagai argumen VM:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

Daftar lengkap properti dapat ditemukan di file ahc-default.properties .

4.1. Permintaan Terikat

Untuk membuat permintaan terikat kita menggunakan metode helper dari kelas AsyncHttpClient yang dimulai dengan awalan "siapkan" . Juga, kita bisa menggunakan metode preparedRequest () yang menerima objek Request yang sudah dibuat .

Misalnya, metode preparedGet () akan membuat permintaan HTTP GET:

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

4.2. Permintaan Tidak Terikat

Permintaan tidak terikat dapat dibuat menggunakan kelas RequestBuilder :

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

atau dengan menggunakan kelas helper Dsl , yang sebenarnya menggunakan RequestBuilder untuk mengonfigurasi metode HTTP dan URL permintaan:

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

5. Menjalankan Permintaan HTTP

Nama perpustakaan memberi kita petunjuk tentang bagaimana permintaan dapat dieksekusi. AHC memiliki dukungan untuk permintaan sinkron dan asinkron.

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:

  • AsyncHandler
  • AsyncCompletionHandler
  • ListenableFuture listeners

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());

Selain itu, opsi untuk menambahkan listener, antarmuka ListenableFuture memungkinkan kita mengubah respons Future menjadi CompletableFuture .

7. Kesimpulan

AHC adalah pustaka yang sangat kuat, dengan banyak fitur menarik. Ini menawarkan cara yang sangat sederhana untuk mengkonfigurasi klien HTTP dan kemampuan menjalankan permintaan sinkron dan asinkron.

Seperti biasa, kode sumber artikel tersedia di GitHub.