如何在JAVA中创建异步HTTP请求?

时间:2010-06-29 16:55:50

标签: java asynchronous httprequest

我对Java很新,所以对某些人来说这似乎是显而易见的。我在ActionScript上做了很多工作,这是非常基于事件的,我很喜欢。我最近尝试编写了一小部分执行POST请求的Java代码,但我遇到了一个问题,即它是一个同步请求,因此代码执行会等待请求完成,超时或出现错误。

如何创建异步请求,代码继续执行,并在HTTP请求完成时调用回调?我瞥了一眼线程,但我觉得它太过分了。

9 个答案:

答案 0 :(得分:48)

Java确实比ActionScript更低级别。这就像比较苹果和橘子。虽然ActionScript在“引擎盖下”透明地处理它,但在Java中,您需要自己管理异步处理(线程)。

幸运的是,在Java中有java.util.concurrent API可以很好地完成这项工作。

您的问题基本上可以解决如下:

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

其中RequestResponse如下所示:

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

public class Response {
    private InputStream body;

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

    public InputStream getBody() {
        return body;
    }
}

另见:

答案 1 :(得分:25)

如果您处于JEE7环境中,您必须有一个不错的JAXRS实现,这将允许您使用其客户端API轻松地发出异步HTTP请求。

这将是这样的:

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

当然,这只是使用期货。如果你可以使用更多的库,你可以看看RxJava,代码看起来像:

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

最后但并非最不重要的是,如果你想重用你的异步调用,你可能想看一下Hystrix,除了一个非常酷的其他东西外,你还可以写下这样的东西: / p>

例如:

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

 }

调用此命令将如下所示:

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:我知道这个帖子已经过时了,但是在没有提到Rx / Hystrix方式的情况下感觉不对。

答案 2 :(得分:23)

您可能还想查看Async Http Client

答案 3 :(得分:13)

根据Apache HTTP Componentsthis SO thread的链接,我遇到了HTTP组件的Fluent外观API。 An example there显示了如何设置异步HTTP请求的队列(并获得完成/失败/取消的通知)。就我而言,我不需要一个队列,一次只需要一个异步请求。

这是我结束的地方(也使用HTTP Components中的URIBuilder,example here)。

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

答案 4 :(得分:6)

您可能想看一下这个问题:Asynchronous IO in Java?

看起来你最好的选择,如果你不想自己争论线程是一个框架。上一篇文章提到 Grizzly,https://grizzly.dev.java.net/和Netty,http://www.jboss.org/netty/

来自netty docs:

Netty项目致力于提供异步事件驱动的网络应用程序框架和工具,以便快速开发可维护的高性能和高可用性。高可扩展性协议服务器&amp;客户端。

答案 5 :(得分:2)

Apache HttpComponents现在也有一个异步的http客户端:

/**
    <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;
    }
  }
}

答案 6 :(得分:2)

请注意,java11现在提供了一个新的HTTP api HttpClient,它使用Java的CompletableFuture支持完全异步的操作。

它还支持同步版本,例如send是同步的,而sendAsync是异步的。

异步请求示例(取自apidoc)

   HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();
   client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);

答案 7 :(得分:1)

必须明确HTTP协议是同步的,这与编程语言无关。客户端发送请求并获得同步响应。

如果您想通过HTTP进行异步行为,则必须构建 over HTTP(我对ActionScript一无所知,但我认为这也是ActionScript所做的)。有许多库可以为您提供此类功能(例如Jersey SSE)。请注意,它们以某种方式定义客户端和服务器之间的依赖关系,因为它们必须就HTTP上方的确切非标准通信方法达成一致。

如果您无法控制客户端和服务器,或者您不希望它们之间存在依赖关系,那么通过HTTP实现异步(例如基于事件的)通信的最常见方法是使用webhooks approach(您可以在java中检查this示例实现。

希望我帮忙!

答案 8 :(得分:0)

这是使用apache HttpClient并在单独的线程中进行调用的解决方案。如果您仅进行一个异步调用,则此解决方案很有用。如果您要打多个电话,建议您使用apache HttpAsyncClient并将这些电话放在线程池中。

import java.lang.Thread;

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;

public class ApacheHttpClientExample {
    public static void main(final String[] args) throws Exception {
        try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
            final HttpGet httpget = new HttpGet("http://httpbin.org/get");
            new Thread(() -> {
                 final String responseBody = httpclient.execute(httpget);
            }).start();
        }
    }
}