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

Je suis assez nouveau en Java, donc cela peut sembler évident pour 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 requête POST, mais j'ai été confronté au problème que c'est une requête synchrone, donc l'exécution du code attend que la requête soit terminée, soit corrigée ou présente une erreur.

Comment puis-je créer une requête asynchrone, où le code continue le l'exécution et un rappel est-il invoqué lorsque la requête HTTP est terminée? J'ai jeté un coup d'oeil à threads, mais je pense que c'est exagéré.

68
demandé sur Ned Batchelder 2010-06-29 20:55:50

7 réponses

Java est en effet plus bas que ActionScript. C'est comme comparer des pommes avec des oranges. Alors Qu'ActionScript gère tout cela de manière transparente "sous le capot", en Java vous devez gérer vous-même le traitement asynchrone (filetage).

heureusement, en Java il y a L'API java.util.concurrent qui peut faire cela d'une manière agréable.

votre problème peut essentiellement ê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 à ce qui suit:

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 aussi:

47
répondu BalusC 2012-03-26 21:24:29

si vous êtes dans un environnement JEE7, vous devez avoir une implémentation décente de JAXRS, ce qui vous permettrait de faire facilement des requêtes HTTP asynchrones en utilisant son API client.

cela ressemble à 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, c'est juste en utilisant des contrats à terme. Si vous êtes D'accord pour utiliser plus de bibliothèques, vous pouvez jeter un oeil à RxJava, le code ressemblerait alors à:

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

et le dernier but pas moins, si vous souhaitez réutiliser votre appel asynchrone, vous voudrez peut-être jeter un oeil à Hystrix, qui - en plus d'un bazillion super cool de d'autres choses - 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);
    }

 }

appelant cette commande ressemblerait à:

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 se sentait mal que personne ne mentionne le chemin Rx/Hystrix dans les réponses up-voted.

23
répondu Psyx 2014-10-30 00:07:21

vous pouvez également consulter Client Http Async .

19
répondu kschneid 2013-03-14 02:59:35

basé sur un lien vers Apache composants HTTP sur ce fil SO , je suis tombé sur L'API de façade fluente pour les composants HTTP. un exemple ici montre comment configurer une file d'attente de requêtes HTTP asynchrones (et être avisé de leur completion/echec/annulation). Dans mon cas, je n'avais pas besoin d'une file d'attente, juste une demande asynchrone à la fois.

voici où j'ai fini (en utilisant aussi URIBuilder 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 () {}
});
12
répondu ericsoco 2017-05-23 11:54:28

vous pouvez jeter un oeil à cette question: asynchrone IO en Java?

il ressemble à votre meilleur pari, si vous ne voulez pas wrangle les fils vous-même est un cadre. 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 est un effort fournir un cadre d'application réseau asynchrone et des outils pour le développement rapide de serveurs et de clients de protocole haute performance et évolutivité.

6
répondu Paul Rubel 2017-05-23 12:02:34

Apache HttpComponents ont aussi un client http async maintenant aussi:

/**
    <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
répondu Dan Brough 2013-07-17 14:20:51

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 requête et obtient une réponse synchrone.

si vous voulez un comportement asynchrone sur HTTP, cela doit être construit sur HTTP (Je ne sais rien à propos D'ActionScript, mais je suppose que c'est ce que L'ActionScript fait aussi). Il existe de nombreuses bibliothèques qui pourraient vous fournir de telles fonctionnalités (par exemple: Maillot de l'ESS ). Notez qu'ils définissent d'une manière ou d'une autre les dépendances entre le client et le serveur car ils doivent s'entendre sur la méthode de communication non standard exacte au-dessus de HTTP.

si vous ne pouvez pas contrôler à la fois le client et le serveur ou si vous ne voulez pas avoir de dépendances entre eux, l'approche la plus commune de la mise en œuvre de la communication asynchrone (par exemple basée sur un événement) sur HTTP est d'utiliser la approche webhooks (vous peut vérifier ce pour un exemple d'implémentation en java).

J'espère avoir aidé!

1
répondu Pantelis Natsiavas 2015-07-01 08:01:46