Asynchrones HTTP mit async-http-client in Java

Asynchrones HTTP mit async-http-client in Java

1. Überblick

AsyncHttpClient (AHC) ist eine Bibliothek, die aufNetty aufbaut, um HTTP-Anforderungen einfach auszuführen und Antworten asynchron zu verarbeiten.

In diesem Artikel wird erläutert, wie Sie den HTTP-Client konfigurieren und verwenden, eine Anforderung ausführen und die Antwort mithilfe von AHC verarbeiten.

2. Konfiguration

Die neueste Version der Bibliothek finden Sie inMaven repository. Wir sollten darauf achten, die Abhängigkeit mit der Gruppen-IDorg.asynchttpclient und nicht mitcom.ning: zu verwenden


    org.asynchttpclient
    async-http-client
    2.2.0

3. HTTP-Client-Konfiguration

Die einfachste Methode zum Abrufen des HTTP-Clients ist die Verwendung der KlasseDsl. Die statischeasyncHttpClient()-Methode gibt einAsyncHttpClient-Objekt zurück:

AsyncHttpClient client = Dsl.asyncHttpClient();

Wenn wir eine benutzerdefinierte Konfiguration des HTTP-Clients benötigen, können wir das ObjektAsyncHttpClientmit dem BuilderDefaultAsyncHttpClientConfig.Builder erstellen:

DefaultAsyncHttpClientConfig.Builder clientBuilder = Dsl.config()

Dies bietet die Möglichkeit, Timeouts, einen Proxy-Server, HTTP-Zertifikate und vieles mehr zu konfigurieren:

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

Sobald wiran instance of the HTTP client we can reuse it across out application konfiguriert und erhalten haben. Wir müssen nicht für jede Anforderung eine Instanz erstellen, da intern neue Threads und Verbindungspools erstellt werden, was zu Leistungsproblemen führt.

Es ist auch wichtig zu beachten, dassonce we’ve finished using the client we should call to close() method to prevent any memory leaks oder hängende Ressourcen.

4. Erstellen einer HTTP-Anfrage

Es gibt zwei Methoden, mit denen wir eine HTTP-Anfrage mit AHC definieren können:

  • gebunden

  • ungebunden

Es gibt keinen wesentlichen Unterschied zwischen den beiden Anforderungstypen in Bezug auf die Leistung. Sie stellen nur zwei separate APIs dar, mit denen wir eine Anfrage definieren können. 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.

Wenn Sie beispielsweise eine gebundene Anforderung erstellen, wird das FlagdisableUrlEncodingaus der HTTP-Client-Konfiguration gelesen, während dies für eine ungebundene Anforderung standardmäßig auf false gesetzt ist. Dies ist nützlich, da die Clientkonfiguration geändert werden kann, ohne die gesamte Anwendung neu zu kompilieren, indem Systemeigenschaften verwendet werden, die als VM-Argumente übergeben werden:

java -jar -Dorg.asynchttpclient.disableUrlEncodingForBoundRequests=true

Eine vollständige Liste der Eigenschaften finden Sie in der Dateiahc-default.properties.

4.1. Gebundene Anfrage

Um eine gebundene Anfrage zu erstellen, verwenden wir die Hilfsmethoden aus der KlasseAsyncHttpClient, die mit dem Präfix“prepare” beginnen. Wir können auch die MethodeprepareRequest()verwenden, die ein bereits erstelltes ObjektRequestempfängt.

Beispielsweise erstellt die MethodeprepareGet()eine HTTP-GET-Anforderung:

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

4.2. Ungebundene Anfrage

Eine ungebundene Anforderung kann mit der KlasseRequestBuildererstellt werden:

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

oder mithilfe der HilfsklasseDsl, die tatsächlichRequestBuilder zum Konfigurieren der HTTP-Methode und der URL der Anforderung verwendet:

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

5. HTTP-Anfragen ausführen

Der Name der Bibliothek gibt uns einen Hinweis darauf, wie die Anforderungen ausgeführt werden können. AHC unterstützt sowohl synchrone als auch asynchrone Anforderungen.

Die Ausführung der Anforderung hängt von ihrem Typ ab. Wenn Sie einebound request we use the execute() method from the BoundRequestBuilder-Klasse verwenden und wenn wir eineunbound request we’ll execute it using one of the implementations of the executeRequest() method from the AsyncHttpClient interface haben.

5.1. Synchron

Die Bibliothek wurde asynchron entworfen, aber bei Bedarf können wir synchrone Aufrufe simulieren, indem wir das ObjektFutureblockieren. Sowohl die Methodenexecute() als auchexecuteRequest() geben einListenableFuture<Response>-Objekt zurück. Diese Klasse erweitert die JavaFuture-Schnittstelle und erbt damit dieget()-S-Methode, mit der der aktuelle Thread blockiert werden kann, bis die HTTP-Anforderung abgeschlossen ist und eine Antwort zurückgegeben wird:

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

Die Verwendung synchroner Aufrufe ist nützlich, wenn Sie versuchen, Teile unseres Codes zu debuggen. Es wird jedoch nicht empfohlen, sie in einer Produktionsumgebung zu verwenden, in der asynchrone Ausführungen zu einer besseren Leistung und einem besseren Durchsatz führen.

5.2. Asynchron

Wenn wir über asynchrone Ausführungen sprechen, sprechen wir auch über Listener für die Verarbeitung der Ergebnisse. Die AHC-Bibliothek bietet drei Arten von Listenern, die für asynchrone HTTP-Aufrufe verwendet werden können:

  • AsyncHandler

  • AsyncCompletionHandler

  • ListenableFuture Zuhörer

Der Listener vonAsyncHandlerbietet die Möglichkeit, den HTTP-Aufruf zu steuern und zu verarbeiten, bevor er abgeschlossen ist. Die Verwendung kann eine Reihe von Ereignissen verarbeiten, die mit dem HTTP-Aufruf zusammenhängen:

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


Mit der AufzählungStatekönnen wir die Verarbeitung der HTTP-Anforderung steuern. Indem wirState.ABORT we can stop the processing zu einem bestimmten Zeitpunkt zurückgeben undState.CONTINUE verwenden, lassen wir die Verarbeitung beenden.

Es ist wichtig zu erwähnen, dassAsyncHandler isn’t thread-safe and shouldn’t be reused when executing concurrent requests.

AsyncCompletionHandler erbt alle Methoden von derAsyncHandler-Schnittstelle und fügt die HilfsmethodeonCompleted(Response) zur Behandlung des Anrufabschlusses hinzu. Alle anderen Listener-Methoden werden überschrieben, umState.CONTINUE zurückzugeben, wodurch der Code besser lesbar wird:

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


Über dieListenableFuture-Schnittstelle können wir Listener hinzufügen, die ausgeführt werden, wenn der HTTP-Aufruf abgeschlossen ist.

Außerdem wird der Code von den Listenern ausgeführt, indem ein anderer Thread-Pool verwendet wird:

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

Mit der Option, Listener hinzuzufügen, können wir über dieListenableFuture-Schnittstelle die Antwort vonFutureinCompletableFuture umwandeln.

7. Fazit

AHC ist eine sehr leistungsfähige Bibliothek mit vielen interessanten Funktionen. Es bietet eine sehr einfache Möglichkeit, einen HTTP-Client zu konfigurieren und sowohl synchrone als auch asynchrone Anforderungen auszuführen.

Wie immer ist der Quellcode für den Artikelover on GitHub verfügbar.