¿Cómo se crea una solicitud HTTP asíncrona en JAVA?

Soy bastante nuevo en Java, por lo que esto puede parecer obvio para algunos. He trabajado mucho con ActionScript, que está basado en eventos y me encanta. Hace poco intenté escribir un pequeño código Java que realiza una solicitud POST, pero me he enfrentado al problema de que se trata de una solicitud síncrona, por lo que la ejecución del código espera a que la solicitud finalice, expire el tiempo o presente un error.

¿Cómo puedo crear una solicitud asincrónica, donde el código continúa la ejecución y se invoca una callback cuando se completa la solicitud HTTP? Eché un vistazo a los hilos, pero estoy pensando que es excesivo.

Java es de hecho más bajo nivel que ActionScript. Es como comparar manzanas con naranjas. Mientras que ActionScript maneja todo de forma transparente “bajo el capó”, en Java usted necesita administrar el procesamiento asíncrono (enhebrado) usted mismo.

Afortunadamente, en Java existe la API java.util.concurrent que puede hacer eso de una manera agradable.

Su problema básicamente se puede resolver de la siguiente manera:

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

donde la Request y la Response son las siguientes:

 public class Request implements Callable { private URL url; public Request(URL url) { this.url = url; } @Override public Response call() throws Exception { return new Response(url.openStream()); } } 

y

 public class Response { private InputStream body; public Response(InputStream body) { this.body = body; } public InputStream getBody() { return body; } } 

Ver también:

  • Lección: Concurrencia : un tutorial de java.util.concurrent .

Si se encuentra en un entorno JEE7, debe tener una implementación decente de JAXRS, lo que le permitiría realizar fácilmente una solicitud HTTP asincrónica utilizando su API de cliente.

Esto se vería así:

 public class Main { public static Future getAsyncHttp(final String url) { return ClientBuilder.newClient().target(url).request().async().get(); } public static void main(String ...args) throws InterruptedException, ExecutionException { Future 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)); } } 

Por supuesto, esto es solo usar futuros. Si está de acuerdo con el uso de algunas bibliotecas más, puede echar un vistazo a RxJava, el código sería así:

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

Y por si fuera poco, si quieres reutilizar tu llamada asincrónica, tal vez quieras echarle un vistazo a Hystrix, que, además de un bazillion súper genial, te permitirá escribir algo como esto:

Por ejemplo:

 public class AsyncGetCommand extends HystrixCommand { 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); } } 

Llamar a este comando se vería así:

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

PD: Sé que el hilo es antiguo, pero se sentía mal que nadie mencionara el modo Rx / Hystrix en las respuestas votadas.

También es posible que desee mirar Async Http Client .

Basado en un enlace a Apache HTTP Components en este hilo SO , me encontré con la fachada API Fluent para HTTP Components. Un ejemplo muestra cómo configurar una cola de solicitudes HTTP asíncronas (y recibir notificaciones de su finalización / falla / cancelación). En mi caso, no necesitaba una cola, solo una solicitud asíncrona a la vez.

Aquí es donde terminé (también estoy usando URIBuilder de HTTP Components, ejemplo aquí ).

 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 future = async.execute(request, new FutureCallback() { 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 () {} }); 

Es posible que desee echar un vistazo a esta pregunta: IO asíncrono en Java?

Parece ser tu mejor apuesta, si no quieres disputar los hilos tú mismo, es un marco. La publicación anterior menciona a Grizzly, https://grizzly.dev.java.net/ y Netty, http://www.jboss.org/netty/ .

De los documentos netty:

El proyecto Netty es un esfuerzo por proporcionar un marco de aplicación de red asíncrono orientado a eventos y herramientas para el rápido desarrollo de servidores y clientes de alto rendimiento y escalabilidad de protocolos de mantenimiento.

Los HttpComponents de Apache ahora también tienen un cliente http asincrónico:

 /**  org.apache.httpcomponents httpasyncclient 4.0-beta4  **/ 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 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 { @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; } } } 

Debe quedar claro que el protocolo HTTP es sincrónico y esto no tiene nada que ver con el lenguaje de progtwigción. El cliente envía una solicitud y obtiene una respuesta sincrónica.

Si desea un comportamiento asíncrono a través de HTTP, debe comstackrse a través de HTTP (no sé nada de ActionScript, pero supongo que esto también es lo que hace ActionScript). Hay muchas bibliotecas que le pueden dar esa funcionalidad (por ejemplo, Jersey SSE ). Tenga en cuenta que de alguna manera definen las dependencias entre el cliente y el servidor, ya que tienen que acordar el método exacto de comunicación no estándar por encima de HTTP.

Si no puede controlar tanto el cliente como el servidor o si no desea tener dependencias entre ellos, el enfoque más común para implementar comunicación asíncrona (por ejemplo, basada en eventos) a través de HTTP es utilizar el enfoque de webhooks (puede verificar esto para obtener una ejemplo de implementación en java).

Espero que haya ayudado!