web-dev-qa-db-fra.com

Comment créer une requête HTTP asynchrone en JAVA?

Je suis assez nouveau sur Java, cela peut donc sembler évident à certains. J'ai beaucoup travaillé avec ActionScript, qui est très basé sur les événements et j'adore ça. J'ai récemment essayé d'écrire un petit morceau de code Java qui fait une demande POST, mais j'ai été confronté au problème qu'il s'agissait d'une demande synchrone, l'exécution du code attend donc que la demande soit complète, expire ou présente une erreur.

Comment créer une demande asynchrone, où le code continue l'exécution et où un rappel est appelé à la fin de la demande HTTP? J'ai jeté un coup d'œil sur les discussions, mais je pense que c'est exagéré.

73
evilpenguin

Java est en effet plus bas niveau qu'ActionScript. C'est comme comparer des pommes avec des oranges. Bien qu'ActionScript gère tout cela "à la dérobade" de manière transparente, vous devez gérer vous-même le traitement asynchrone (threading) dans Java.

Heureusement, dans Java, il existe une API Java.util.concurrent qui peut le faire de manière agréable.

Votre problème peut fondamentalement être résolu comme suit:

// Have one (or more) threads ready to do the async tasks. Do this during startup of your app.
ExecutorService executor = Executors.newFixedThreadPool(1); 

// Fire a request.
Future<Response> response = executor.submit(new Request(new URL("http://google.com")));

// Do your other tasks here (will be processed immediately, current thread won't block).
// ...

// Get the response (here the current thread will block until response is returned).
InputStream body = response.get().getBody();
// ...

// Shutdown the threads during shutdown of your app.
executor.shutdown();

Request et Response ressemblent à ceci:

public class Request implements Callable<Response> {
    private URL url;

    public Request(URL url) {
        this.url = url;
    }

    @Override
    public Response call() throws Exception {
        return new Response(url.openStream());
    }
}

et

public class Response {
    private InputStream body;

    public Response(InputStream body) {
        this.body = body;
    }

    public InputStream getBody() {
        return body;
    }
}

Voir également:

47
BalusC

Si vous êtes dans un environnement JEE7, vous devez avoir une implémentation décente de JAXRS, ce qui vous permettrait de faire facilement une requête HTTP asynchrone à l'aide de son API client.

Cela ressemblerait à ceci:

public class Main {

    public static Future<Response> getAsyncHttp(final String url) {
        return ClientBuilder.newClient().target(url).request().async().get();
    }

    public static void main(String ...args) throws InterruptedException, ExecutionException {
        Future<Response> response = getAsyncHttp("http://www.nofrag.com");
        while (!response.isDone()) {
            System.out.println("Still waiting...");
            Thread.sleep(10);
        }
        System.out.println(response.get().readEntity(String.class));
    }
}

Bien sûr, il ne s’agit que d’avenir. Si vous êtes prêt à utiliser d'autres bibliothèques, vous pouvez jeter un coup d'œil à RxJava, le code ressemblerait alors à ceci:

public static void main(String... args) {
    final String url = "http://www.nofrag.com";
    rx.Observable.from(ClientBuilder.newClient().target(url).request().async().get(String.class), Schedulers
            .newThread())
            .subscribe(
                    next -> System.out.println(next),
                    error -> System.err.println(error),
                    () -> System.out.println("Stream ended.")
            );
    System.out.println("Async proof");
}

Enfin, si vous souhaitez réutiliser votre appel asynchrone, jetez un coup d’œil à Hystrix, qui - en plus d’un bazillion d’autres trucs super cool - vous permettrait d’écrire quelque chose comme ceci:

Par exemple:

public class AsyncGetCommand extends HystrixCommand<String> {

    private final String url;

    public AsyncGetCommand(final String url) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HTTP"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationThreadTimeoutInMilliseconds(5000)));
        this.url = url;
    }

    @Override
    protected String run() throws Exception {
        return ClientBuilder.newClient().target(url).request().get(String.class);
    }

 }

L'appel de cette commande ressemblerait à ceci:

public static void main(String ...args) {
    new AsyncGetCommand("http://www.nofrag.com").observe().subscribe(
            next -> System.out.println(next),
            error -> System.err.println(error),
            () -> System.out.println("Stream ended.")
    );
    System.out.println("Async proof");
}

PS: Je sais que le fil est vieux, mais il était faux de constater que personne ne mentionne la façon dont Rx/Hystrix est mentionné dans les réponses plus votées.

27
Psyx

Vous pouvez également vouloir regarder Client HTTP Async .

24
kschneid

Basé sur un lien vers Apache HTTP Components sur this SO thread) , je suis tombé sur l'API de façade Fluent pour composants HTTP. n exemple ici montre comment configurer une file d'attente de requêtes HTTP asynchrones (et recevoir une notification de leur achèvement/échec/annulation). Dans mon cas, je n'avais pas besoin d'une file d'attente, mais d'une requête async à la fois. .

Voici où je me suis retrouvé (en utilisant également URIBuilder à partir de composants HTTP, exemple ici ).

import Java.net.URI;
import Java.net.URISyntaxException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
import org.Apache.http.client.fluent.Async;
import org.Apache.http.client.fluent.Content;
import org.Apache.http.client.fluent.Request;
import org.Apache.http.client.utils.URIBuilder;
import org.Apache.http.concurrent.FutureCallback;

//...

URIBuilder builder = new URIBuilder();
builder.setScheme("http").setHost("myhost.com").setPath("/folder")
    .setParameter("query0", "val0")
    .setParameter("query1", "val1")
    ...;
URI requestURL = null;
try {
    requestURL = builder.build();
} catch (URISyntaxException use) {}

ExecutorService threadpool = Executors.newFixedThreadPool(2);
Async async = Async.newInstance().use(threadpool);
final Request request = Request.Get(requestURL);

Future<Content> future = async.execute(request, new FutureCallback<Content>() {
    public void failed (final Exception e) {
        System.out.println(e.getMessage() +": "+ request);
    }
    public void completed (final Content content) {
        System.out.println("Request completed: "+ request);
        System.out.println("Response:\n"+ content.asString());
    }

    public void cancelled () {}
});
13
ericsoco

Vous voudrez peut-être jeter un coup d’œil à cette question: Asynchrone IO en Java?

Il semble que votre meilleur pari, si vous ne voulez pas vous disputer vous-même est un framework. Le post précédent mentionne Grizzly, https://grizzly.dev.Java.net/ , et Netty, http://www.jboss.org/netty/ .

De la netty docs:

Le projet Netty vise à fournir un cadre d’application réseau asynchrone et des outils pour le développement rapide de serveurs et de clients de protocole hautement évolutifs et hautement évolutifs.

6
Paul Rubel

Apache HttpComponents possède également un client http async maintenant:

/**
    <dependency>
      <groupId>org.Apache.httpcomponents</groupId>
      <artifactId>httpasyncclient</artifactId>
      <version>4.0-beta4</version>
    </dependency>
**/

import Java.io.IOException;
import Java.nio.CharBuffer;
import Java.util.concurrent.Future;

import org.Apache.http.HttpResponse;
import org.Apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.Apache.http.impl.nio.client.HttpAsyncClients;
import org.Apache.http.nio.IOControl;
import org.Apache.http.nio.client.methods.AsyncCharConsumer;
import org.Apache.http.nio.client.methods.HttpAsyncMethods;
import org.Apache.http.protocol.HttpContext;

public class HttpTest {

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

    final CloseableHttpAsyncClient httpclient = HttpAsyncClients
        .createDefault();
    httpclient.start();
    try {
      final Future<Boolean> future = httpclient.execute(
          HttpAsyncMethods.createGet("http://www.google.com/"),
          new MyResponseConsumer(), null);
      final Boolean result = future.get();
      if (result != null && result.booleanValue()) {
        System.out.println("Request successfully executed");
      } else {
        System.out.println("Request failed");
      }
      System.out.println("Shutting down");
    } finally {
      httpclient.close();
    }
    System.out.println("Done");
  }

  static class MyResponseConsumer extends AsyncCharConsumer<Boolean> {

    @Override
    protected void onResponseReceived(final HttpResponse response) {
    }

    @Override
    protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl)
        throws IOException {
      while (buf.hasRemaining()) {
        System.out.print(buf.get());
      }
    }

    @Override
    protected void releaseResources() {
    }

    @Override
    protected Boolean buildResult(final HttpContext context) {
      return Boolean.TRUE;
    }
  }
}
2
Dan Brough

Il faut préciser que le protocole HTTP est synchrone et que cela n’a rien à voir avec le langage de programmation. Le client envoie une demande et obtient une réponse synchrone.

Si vous voulez un comportement asynchrone sur HTTP, il faut le construire sur HTTP (je ne connais rien à ActionScript mais je suppose que c'est ActionScript également). De nombreuses bibliothèques pourraient vous fournir une telle fonctionnalité (par exemple, Jersey SSE ). Notez qu'ils définissent en quelque sorte les dépendances entre le client et le serveur car ils doivent se mettre d'accord sur la méthode de communication non standard exacte supérieure à HTTP.

Si vous ne pouvez pas contrôler à la fois le client et le serveur ou si vous ne souhaitez pas avoir de dépendances entre eux, l'approche la plus courante d'implémentation d'une communication asynchrone (basée sur des événements, par exemple) via HTTP consiste à utiliser approche à l'aide de Webhooks (vous pouvez vérifier this pour un exemple d'implémentation en Java).

J'espère que j'ai aidé!

1
Pantelis Natsiavas