web-dev-qa-db-ja.com

JAVAで非同期HTTPリクエストを作成するにはどうすればよいですか?

私はJavaにかなり慣れていないので、これは一部の人には明白に思えるかもしれません。私はActionScriptで多くの仕事をしてきましたが、これは非常にイベントベースであり、それが大好きです。最近、Javaリクエストを行うPOSTコードを少し書き込もうとしましたが、同期リクエストであるという問題に直面したため、コードの実行は待機します完了、タイムアウト、またはエラーの表示のリクエスト。

コードが実行を継続し、HTTPリクエストが完了するとコールバックが呼び出される非同期リクエストを作成するにはどうすればよいですか?私はスレッドを一見したが、やり過ぎだと思っている。

73
evilpenguin

実際、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();

ここで、RequestResponseは次のようになります。

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

こちらもご覧ください:

47
BalusC

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を見てみたいと思うかもしれません。

例えば:

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の方法に言及している人がいないのは間違っていると感じました。

27
Psyx

Async Http Client もご覧ください。

24
kschneid

Apache HTTP Components on this SO thread へのリンクに基づいて、HTTPコンポーネントのFluentファサードAPIに出会いました。 は、非同期HTTPリクエストのキューを設定する方法を示しています(そして、完了/失敗/キャンセルの通知を受け取ります)。私の場合、キューは不要で、非同期リクエストは一度に1つだけでした。

最終的にここに行きました(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 () {}
});
13
ericsoco

次の質問をご覧ください: 非同期IO in Java?

スレッドを自分でフレームワークにしたくない場合は、最善の方法のように見えます。前の投稿では、Grizzly、 https://grizzly.dev.Java.net/ 、およびNetty、 http://www.jboss.org/netty/ に言及しています。

Netty docsから:

Nettyプロジェクトは、非同期イベント駆動型ネットワークアプリケーションフレームワークとツールを提供して、保守可能な高性能および高スケーラビリティのプロトコルサーバーおよびクライアントを迅速に開発するための取り組みです。

6
Paul Rubel

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;
    }
  }
}
2
Dan Brough

HTTPプロトコルが同期的であり、プログラミング言語とは何の関係もないことを明確にする必要があります。クライアントは要求を送信し、同期応答を取得します。

HTTPを介した非同期動作が必要な場合は、これを構築する必要がありますoverHTTP(ActionScriptについては何も知りませんが、これはActionScriptも何をするか)。このような機能を提供できるライブラリは多数あります(例: Jersey SSE )。 HTTPを介した厳密な非標準通信方法に同意する必要があるため、クライアントとサーバー間の依存関係を何らかの形で定義することに注意してください。

クライアントとサーバーの両方を制御できない場合、またはそれらの間に依存関係を持ちたくない場合、HTTPを介した非同期(イベントベースなど)通信を実装する最も一般的なアプローチは、 webhooks approach を使用することです(Javaの実装例については、 this を確認できます)。

私が助けたことを願っています!

1