你如何在JAVA中创build一个asynchronous的HTTP请求?
我对Java相当陌生,所以这对一些人来说可能是显而易见的。 我用ActionScript做了很多工作,这很基于事件,我喜欢。 我最近试图编写一小段执行POST请求的Java代码,但是我一直面临着这是一个同步请求的问题,因此代码执行会等待请求完成,超时或出现错误。
我如何创build一个asynchronous请求,代码继续执行,并在HTTP请求完成时调用callback? 我已经看过线程,但我认为这是过度的。
Java确实比ActionScript更低级。 这就像比较苹果和橘子。 尽pipeActionScript在“底层”透明地处理它,但在Java中,您需要自己pipe理asynchronous处理(线程)。
幸运的是,在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();
其中Request
和Response
如下所示:
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; } }
也可以看看:
- 课程:并发 – 一个
java.util.concurrent
教程。
如果您处于JEE7环境中,则必须有一个体面的JAXRS实现挂起,这将允许您使用其客户端API轻松地进行asynchronousHTTP请求。
这看起来像这样:
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"); }
最后但并非最不重要的是,如果你想重复使用你的asynchronous调用,你可能需要看看Hystrix,除了超级超酷的其他东西外,还可以让你写这样的东西:
例如:
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方法,这是错误的。
你也可能想看看asynchronousHttp客户端 。
基于此SO线程上的Apache HTTP组件的链接,我遇到了HTTP组件的Fluent facade API。 这里的一个例子展示了如何build立一个asynchronousHTTP请求的队列(并获得完成/失败/取消的通知)。 在我的情况下,我不需要一个队列,一次只需要一个asynchronous请求。
这里是我结束的地方(同样使用来自HTTP组件的URIBuilder, 这里的例子 )。
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 () {} });
你可能想看看这个问题: Java中的asynchronousIO?
它看起来像你最好的select,如果你不想自己纠缠线程是一个框架。 上一篇文章提到了Grizzly, https ://grizzly.dev.java.net/和Netty, http: //www.jboss.org/netty/。
从netty文档:
Netty项目旨在提供asynchronous事件驱动的networking应用程序框架和工具,以便快速开发可维护的高性能和高可扩展性协议服务器和客户端。
Apache HttpComponents现在也有一个asynchronoushttp客户端:
/** <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; } } }
必须明确HTTP协议是同步的,这与编程语言无关。 客户端发送请求并获得同步响应。
如果你想要通过HTTP进行asynchronous行为,那么必须通过 HTTP构build(我对ActionScript没有任何了解,但我想这也是ActionScript所做的)。 有许多库可以给你这样的function(例如Jersey SSE )。 请注意,它们以某种方式定义了客户端和服务器之间的依赖关系,因为它们必须同意HTTP之上的确切的非标准通信方法。
如果你不能控制客户端和服务器,或者你不想控制它们之间的依赖关系,通过HTTP实现asynchronous(如基于事件)通信的最常见的方法是使用webhooks方法 (你可以检查这个在java中的示例实现)。
希望我帮助!