Как создать асинхронный HTTP-запрос в JAVA?
Я довольно новыми для Java, так что это может показаться очевидным для некоторых. Я много работал с ActionScript, который очень основан на событиях, и мне это нравится. Недавно я попытался написать небольшой фрагмент кода Java, который выполняет запрос POST, но я столкнулся с проблемой, что это синхронный запрос, поэтому выполнение кода ожидает завершения запроса, тайм-аута или появления ошибки.
Как я могу создать асинхронный запрос, где код продолжает выполнение и обратный вызов вызывается, когда HTTP-запрос завершен? Я посмотрел на нити, но я думаю, что это перебор.
7 ответов:
Java действительно более низкий уровень, чем ActionScript. Это все равно что сравнивать яблоки с апельсинами. Хотя ActionScript обрабатывает все это прозрачно "под капотом", в Java вам нужно самостоятельно управлять асинхронной обработкой (потоковой обработкой).
к счастью, в Java есть
java.util.concurrentAPI, который может сделать это в хорошей манере.ваша проблема может быть решена следующим образом:
// 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; } }Читайте также:
- Урока: Параллельность - a
java.util.concurrentучебник.
если вы находитесь в среде JEE7, у вас должна быть приличная реализация jaxrs, которая позволит вам легко сделать асинхронный HTTP-запрос, используя его клиентский API.
это будет выглядеть так:
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, который-в дополнение к bazillion super cool другим вещам - позволит вам написать что-то вроде этого:
например:
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 в голосовании за ответы.
вы также можете посмотреть Асинхронный Http-Клиент.
основана на ссылке Apache HTTP Components on это так нити, я наткнулся на Fluent facade API для HTTP-компонентов. пример есть показывает, как создать очередь асинхронных HTTP-запросов (и получать уведомления об их завершении/отказ/отмена). В моем случае мне не нужна была очередь, только один асинхронный запрос за раз.
вот где я оказался (также используя URIBuilder из HTTP-компонентов,пример здесь).
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?
похоже, ваш лучший выбор, если вы не хотите, чтобы управляться с потоками-это основа. Предыдущий пост упоминает Гризли,https://grizzly.dev.java.net/, и Нетти,http://www.jboss.org/netty/.
от Нетти документы:
проект Netty-это попытка обеспечить асинхронное сетевое приложение, управляемое событиями рамки и инструменты для быстрого развития ремонтопригодных серверов и клиентов протокола высокой производительности и высокой масштабируемости.
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; } } }
должно быть ясно, что протокол HTTP является синхронным, и это не имеет ничего общего с языком программирования. Клиент отправляет запрос и получает синхронный ответ.
Если вы хотите асинхронного поведения по HTTP, это должно быть построено over HTTP (я ничего не знаю о ActionScript, но я полагаю, что это то, что ActionScript тоже делает). Есть много библиотек, которые могут дать вам такую функциональность (например,Джерси SSE). Обратите внимание, что они каким-то образом определяют зависимости между клиентом и сервером, поскольку они должны согласовать точный нестандартный метод связи выше HTTP.
Если вы не можете управлять как клиентом, так и сервером, или если вы не хотите иметь зависимости между ними, наиболее распространенным подходом к реализации асинхронной (например, основанной на событиях) связи через HTTP является использование веб-перехватчиков подход (вы можете проверить этой для примера реализация на java).
надеюсь, что помогла!
Comments