आप जावा में अतुल्यकालिक HTTP अनुरोध कैसे बनाते हैं?


81

मैं जावा के लिए काफी नया हूं, इसलिए यह कुछ स्पष्ट लग सकता है। मैंने एक्शनस्क्रिप्ट के साथ बहुत काम किया है, जो बहुत अधिक घटना आधारित है और मुझे यह पसंद है। मैंने हाल ही में एक छोटा सा Java कोड लिखने की कोशिश की है जो एक POST रिक्वेस्ट करता है, लेकिन मुझे इस समस्या का सामना करना पड़ा है कि यह एक सिंक्रोनस रिक्वेस्ट है, इसलिए कोड एक्जीक्यूशन पूरा होने, टाइम आउट या एरर पेश करने के लिए वेट करता है।

मैं एक अतुल्यकालिक अनुरोध कैसे बना सकता हूं, जहां कोड निष्पादन को जारी रखता है और HTTP अनुरोध पूरा होने पर कॉलबैक लागू किया जाता है? मैंने थ्रेड्स पर गौर किया है, लेकिन मैं सोच रहा हूं कि यह ओवरकिल है।


जवाबों:


11

ध्यान दें कि java11 अब एक नया HTTP एपीआई प्रदान करता है HttpClient है, जो पूरी तरह से समर्थन करता है अतुल्यकालिक आपरेशन, जावा का उपयोग CompletableFuture

यह भी तरह कॉल के साथ एक तुल्यकालिक संस्करण का समर्थन करता है, भेजने , जो सिंक्रोनस होता है और sendAsync , जो अतुल्यकालिक है।

एक async अनुरोध का उदाहरण (एपिडोक से लिया गया):

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

1
अगर मैं java8 का उपयोग करता हूं, तो कौन सा एपीआई सबसे अच्छा है?
एलन

@ मुझे पता नहीं है उम्मीद है कि जल्द ही हर कोई java11 का उपयोग कर सकता है ...
इमैनुएल टौजरी

31

यदि आप एक 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");
}

और अंतिम, लेकिन कम से कम, यदि आप अपने एसिक्स कॉल का पुन: उपयोग नहीं करना चाहते हैं, तो आप हिस्ट्रिक्स पर एक नज़र डालना चाह सकते हैं, जो - एक बिलियन सुपर कूल अन्य सामान के अलावा - आपको इस तरह से कुछ लिखने की अनुमति देगा:

उदाहरण के लिए:

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

पुनश्च: मुझे पता है कि धागा पुराना है, लेकिन यह गलत लगा कि किसी ने भी वोटिंग के जवाब में आरएक्स / हिस्ट्रिक्स के तरीके का उल्लेख नहीं किया है।


मैं इसे प्रॉक्सी के साथ कैसे उपयोग कर सकता हूं?
देजेल

बहुत अच्छा होगा यदि आप इस उत्तर पर विशेष रूप से RxJava उदाहरण के बारे में विस्तार से ध्यान दें, मुझे newTread () के लिए एक मेथडॉल दिखाई दे रहा है, जो लगता है कि यह कोड भी एक नया धागा है? मैं अस्पष्ट रूप से आरएक्स की एसिंक्स कैपेबिल्टी से परिचित हूं, इसलिए मुझे इस तरह का आश्चर्य ...
एंडर्स मार्टिनी

शेड्यूलर .newThread () कॉल केवल Rx को इस मामले में एक नए थ्रेड पर निष्पादन को स्पिन करने के लिए कहता है - यह async कंप्यूटिंग लागू करता है। बेशक, यदि आपके पास पहले से ही किसी प्रकार का एसिंक्स सेटअप है, तो आप इसे बहुत आसानी से उपयोग कर सकते हैं (Scheduler.from (Executor) ध्यान में आता है)।
Psyx

1
@Gank हाँ, चूंकि यह लैम्ब्डा का उपयोग करता है, यह 1.8 से अधिक का संकलन नहीं कर सकता है। उपभोक्ता आदि का उपयोग करके इसे लंबा लिखना आसान होना चाहिए ...
Psyx

@psyx क्या हमें अवलोकन योग्य के लिए सदस्यता समाप्त करनी है?
निक गैलिमोर


14

इस SO थ्रेड पर Apache HTTP Components के लिंक के आधार पर , मैं HTTP घटकों के लिए धाराप्रवाह एपीआई के पार आया। एक उदाहरण दिखाता है कि अतुल्यकालिक HTTP अनुरोधों की एक कतार कैसे स्थापित की जाए (और उनके पूरा होने / विफलता / रद्द करने के बारे में सूचित करें)। मेरे मामले में, मुझे एक कतार की आवश्यकता नहीं थी, एक समय में सिर्फ एक async अनुरोध।

यहां मैं समाप्त हुआ (यहां तक ​​कि 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 () {}
});

6

आप इस प्रश्न पर एक नज़र रखना चाहते हैं: जावा में अतुल्यकालिक IO?

यह आपकी सबसे अच्छी शर्त की तरह दिखता है, यदि आप थ्रेड्स को ख़राब नहीं करना चाहते हैं तो यह एक ढांचा है। पिछली पोस्ट में ग्रिजली, https://grizzly.dev.java.net/ , और Netty, http://www.jboss.org/netty/ का उल्लेख किया गया है ।

नेट्टी डॉक्स से:

द नेटी प्रोजेक्ट एक अतुल्यकालिक ईवेंट-चालित नेटवर्क एप्लिकेशन फ्रेमवर्क और उपकरण बनाए रखने का प्रयास है, जो उच्च प्रदर्शन और उच्च स्केलेबिलिटी प्रोटोकॉल सर्वर और क्लाइंट के तेजी से विकास के लिए है।


2

Apache HttpCompords के पास अब एक async 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;
    }
  }
}

मैं इसे प्रॉक्सी के साथ कैसे उपयोग कर सकता हूं?
18

@ डीजेल मुझे लगता है कि आप सिस्टम के गुणों को यहाँ निर्दिष्ट करेंगे: docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
Dan Brough

1
Future.get () कॉल करने से थ्रेड ब्लॉक हो जाएगा। वास्तव में async होने के लिए इसे किसी अन्य थ्रेड में रखने की आवश्यकता है। HttpAsyncClients लाइब्रेरी का नाम खराब है ...
jjbskir

1

यह स्पष्ट करना होगा कि HTTP प्रोटोकॉल समकालिक है और इसका प्रोग्रामिंग भाषा से कोई लेना-देना नहीं है। क्लाइंट एक अनुरोध भेजता है और एक तुल्यकालिक प्रतिक्रिया प्राप्त करता है।

यदि आप HTTP पर एक अतुल्यकालिक व्यवहार करना चाहते हैं, तो इसे HTTP पर बनाया जाना चाहिए (मुझे ActionScript के बारे में कुछ भी नहीं पता है, लेकिन मुझे लगता है कि यह वही है जो ActionScript भी करता है)। कई पुस्तकालय हैं जो आपको इस तरह की कार्यक्षमता दे सकते हैं (जैसे जर्सी एसएसई )। ध्यान दें कि वे किसी भी तरह क्लाइंट और सर्वर के बीच निर्भरता को परिभाषित करते हैं क्योंकि उन्हें HTTP के ऊपर सटीक गैर मानक संचार विधि पर सहमत होना पड़ता है।

आप दोनों क्लाइंट और सर्वर पर नियंत्रण नहीं कर सकते हैं या यदि आप उन दोनों के बीच निर्भरता के लिए नहीं करना चाहते, HTTP पर अतुल्यकालिक (जैसे घटना आधारित) संचार को लागू उपयोग कर रहा है की सबसे आम तरीका है webhooks दृष्टिकोण (आप जाँच कर सकते हैं इस एक के लिए उदाहरण जावा में कार्यान्वयन)।

आशा है कि मैंने मदद की!


जबकि तकनीकी रूप से सच है, यह जवाब भ्रामक हो सकता है क्योंकि सर्वर या HTTP प्रोटोकॉल का समर्थन करने के बावजूद, क्लाइंट कार्यान्वयन के बहुत महत्वपूर्ण प्रदर्शन निहितार्थ हो सकते हैं, भले ही यह उसी थ्रेड पर अवरुद्ध तरीके से अनुरोध को निष्पादित कर रहा हो, एक अलग थ्रेड में एक थ्रेड पूल, या आदर्श रूप से गैर-अवरुद्ध IO (NIO) का उपयोग करते हुए जहां कॉलिंग थ्रेड तब तक सोता है जब तक कि प्रतिक्रिया आने पर ओएस इसे नहीं उठाता। ऐसा लगता है कि ओपी प्रोटोकॉल के बजाय क्लाइंट थ्रेडिंग मॉडल में रुचि रखता है।
15

0

यहाँ अपाचे HttpClient का उपयोग करके और एक अलग थ्रेड में कॉल करने का एक समाधान है । यह समाधान उपयोगी है यदि आप केवल एक एसिंक्स कॉल कर रहे हैं। यदि आप एक से अधिक कॉल कर रहे हैं, तो मैं अपाचे 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();
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.