我对Java很新,所以对某些人来说这似乎是显而易见的。我在ActionScript上做了很多工作,这是非常基于事件的,我很喜欢。我最近尝试编写了一小部分执行POST请求的Java代码,但我遇到了一个问题,即它是一个同步请求,因此代码执行会等待请求完成,超时或出现错误。
如何创建异步请求,代码继续执行,并在HTTP请求完成时调用回调?我瞥了一眼线程,但我觉得它太过分了。
答案 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();
其中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
教程。答案 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 Components上this 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();
}
}
}