Jestem całkiem nowy w Javie, więc niektórym może się to wydawać oczywiste. Dużo pracowałem z ActionScriptem, który jest oparty na zdarzeniach i bardzo mi się to podoba. Niedawno próbowałem napisać mały fragment kodu Java, który wykonuje żądanie POST, ale napotkałem problem, że jest to żądanie synchroniczne, więc wykonanie kodu czeka na zakończenie żądania, przekroczenie limitu czasu lub wyświetlenie błędu.
Jak mogę utworzyć żądanie asynchroniczne, w którym kod kontynuuje wykonywanie, a wywołanie zwrotne jest wywoływane po zakończeniu żądania HTTP? Zerknąłem na nici, ale myślę, że to przesada.
java
asynchronous
httprequest
zły pingwin
źródło
źródło
Odpowiedzi:
Zwróć uwagę, że java11 oferuje teraz nowy HTTP api HttpClient , który obsługuje w pełni asynchroniczne operacje przy użyciu CompletableFuture języka Java .
Obsługuje również wersję synchroniczną z wywołaniami, takimi jak send , który jest synchroniczny, i sendAsync , który jest asynchroniczny.
Przykład żądania asynchronicznego (pobrane z 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);
źródło
Jeśli jesteś w środowisku JEE7, musisz mieć przyzwoitą implementację JAXRS, która umożliwiłaby łatwe tworzenie asynchronicznych żądań HTTP za pomocą jego klienta API.
Wyglądałoby to tak:
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)); } }
Oczywiście jest to po prostu wykorzystanie futures. Jeśli nie masz nic przeciwko używaniu większej liczby bibliotek, możesz rzucić okiem na RxJava, kod wyglądałby wtedy następująco:
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"); }
I wreszcie, jeśli chcesz ponownie użyć wywołania asynchronicznego, możesz rzucić okiem na Hystrix, który - oprócz miliardów super fajnych innych rzeczy - pozwoliłby ci napisać coś takiego:
Na przykład:
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); } }
Wywołanie tego polecenia wyglądałoby tak:
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: Wiem, że wątek jest stary, ale czułem się źle, że nikt nie wspomina o sposobie Rx / Hystrix w odpowiedziach głosowanych w górę.
źródło
Możesz również spojrzeć na Async Http Client .
źródło
Bazując na linku do Apache HTTP Components w tym wątku SO , natknąłem się na Fluent Fasada API dla komponentów HTTP. Przykład tam pokazuje, jak ustawić kolejkę asynchronicznych żądań HTTP (i otrzymywać powiadomienia o ich zakończeniu / niepowodzeniu / anulowaniu). W moim przypadku nie potrzebowałem kolejki, tylko jedno żądanie asynchroniczne na raz.
Oto, gdzie skończyłem (również używając URIBuilder z HTTP Components, przykład tutaj ).
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 () {} });
źródło
Możesz spojrzeć na to pytanie: Asynchroniczne operacje we / wy w Javie?
Wygląda na to, że najlepiej jest założyć, że jeśli nie chcesz samodzielnie walczyć z wątkami, jest to framework. Poprzedni post wspomina o Grizzly, https://grizzly.dev.java.net/ i Netty, http://www.jboss.org/netty/ .
Z dokumentów Netty:
Projekt Netty jest próbą dostarczenia asynchronicznego szkieletu aplikacji sieciowych sterowanych zdarzeniami oraz narzędzi do szybkiego tworzenia łatwych w utrzymaniu serwerów i klientów protokołów o wysokiej wydajności i skalowalności.
źródło
Apache HttpComponents ma teraz również asynchronicznego klienta 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; } } }
źródło
Należy wyjaśnić, że protokół HTTP jest synchroniczny i nie ma to nic wspólnego z językiem programowania. Klient wysyła żądanie i otrzymuje synchroniczną odpowiedź.
Jeśli chcesz zachować asynchroniczne zachowanie przez HTTP, to musi to być zbudowane przez HTTP (nie wiem nic o ActionScript, ale przypuszczam, że to samo robi ActionScript). Istnieje wiele bibliotek, które mogą zapewnić taką funkcjonalność (np. Jersey SSE ). Zauważ, że w jakiś sposób definiują zależności między klientem a serwerem, ponieważ muszą uzgodnić dokładną niestandardową metodę komunikacji powyżej HTTP.
Jeżeli nie można sterować zarówno klienta jak i serwera lub jeśli nie chcemy mieć współzależności między nimi, najczęściej podejście wdrożenia asynchroniczny (np oparciu zdarzeń) Komunikacja za pośrednictwem protokołu HTTP używa zbliżyć webhooks (można sprawdzić to za przykładowa implementacja w java).
Mam nadzieję, że pomogłem!
źródło
Oto rozwiązanie wykorzystujące apache HttpClient i nawiązywanie połączenia w osobnym wątku. To rozwiązanie jest przydatne, jeśli wykonujesz tylko jedno połączenie asynchroniczne. Jeśli wykonujesz wiele wywołań, sugeruję użycie apache HttpAsyncClient i umieszczenie wywołań w puli wątków.
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(); } } }
źródło