JAVAで非同期HTTPリクエストをどのように作成しますか?


81

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

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


回答:


11

java11は今、新しいHTTP API提供していHttpClientをJavaの使用して、そのサポート完全に非同期操作をCompletableFutureを

また、同期バージョンのsend、同期のsendsendAsyncなどの呼び出しもサポートしています(非同期)います。

非同期リクエストの例(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);

1
java8を使用する場合、どのAPIが最適ですか?
アレン

@alenわからない。うまくいけば、すぐに誰もが... java11使用することができます
エマニュエルTouzery

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

最後になりましたが、非同期呼び出しを再利用したい場合は、Hystrixを確認することをお勧めします。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の方法について誰も言及していないのは間違っていると感じました。


プロキシでどのように使用できますか?
dejell 2015年

この答え、特にRxJavaの例について詳しく説明すると、newThread()へのメソッド呼び出しが表示されます。これは、このコードも新しいスレッドを起動していることを意味しているようです。私はRxの非同期機能に漠然と精通しているので、この種の驚きは...
Anders Martini

Scheduler.newThread()呼び出しは、この場合、新しいスレッドで実行をスピンするようにRxに指示するだけです。これにより、非同期コンピューティングが強制されます。もちろん、すでに何らかの非同期設定がある場合は、代わりにかなり簡単に使用できます(Scheduler.from(Executor)が思い浮かびます)。
Psyx 2017

1
@Gankはい、ラムダを使用しているため、1.8を超えてコンパイルすることはできません。それを消費者などを使用して長い道のり...書きやすい十分なはずです
Psyx

@psyx observableの購読を解除する必要がありますか?
NickGallimore20年


14

リンクに基づいてのApache HTTPコンポーネント、このSOスレッド、私は、HTTPコンポーネントの流暢ファサード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 () {}
});

6

あなたはこの質問を見てみたいと思うかもしれません: Javaの非同期IO?

自分でスレッドを混乱させたくない場合は、フレームワークが最善の策のようです。前回の投稿では、Grizzly(https://grizzly.dev.java.net/)とNetty(http://www.jboss.org/netty/)について言及しています。

nettyドキュメントから:

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


2

ApacheHttpComponentsにも非同期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;
    }
  }
}

プロキシでどのように使用できますか?
dejell 2015年

@Dejelここで指定されているようにシステムプロパティを設定すると仮定します:docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
Dan Brough

1
future.get()を呼び出すと、スレッドがブロックされます。実際に非同期にするには、これを別のスレッドに配置する必要があります。HttpAsyncClientsライブラリが悪い...という名前です
jjbskir

1

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

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

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

私が助けてくれたらいいのに!


技術的には正しいですが、サーバーまたはHTTPプロトコルが何をサポートしているかに関係なく、クライアントの実装は、同じスレッドでリクエストをブロックする方法で実行しているかどうかによって、パフォーマンスに非常に大きな影響を与える可能性があるため、誤解を招く可能性があります。スレッドプール、または理想的には非ブロッキングIO(NIO)を使用します。この場合、呼び出し元のスレッドは、応答が到着したときにOSがウェイクアップするまでスリープします。OPは、プロトコルではなくクライアントスレッドモデルに関心があるようです。
GEG

0

これは、Apache HttpClientを使用し、別のスレッドで呼び出しを行うソリューションです。このソリューションは、非同期呼び出しを1回だけ行う場合に役立ちます。複数の呼び出しを行う場合は、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();
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.