web-dev-qa-db-ja.com

OkHttpは、スレッド処理を使用せずに、一見同期的なHTTP接続で並列HTTPリクエストをどのように実行しますか?

OkHttp ライブラリを使用していくつかのパフォーマンステストを行ったところ、すばらしいことがわかりました。 http://httpbin.org/delay/1 への80件のリクエストを実行しました。これにより、私のHTC One電話で4.7秒に、リクエストごとに1秒ずつ意図的に一時停止します。私はコードを見て、なぜそれがとても速いのかを見つけようとしています。開発者(Square Inc)は、接続プーリングと非同期呼び出しを宣伝しています。どちらも優れたパフォーマンスに貢献していると思います。

私は.NETの世界から来ており、.NET 4.5では、非同期 GetResponse-method を備えた真の非同期HTTPライブラリがあります。応答を待つ間にOSにスレッドを譲ることにより、リソースを解放して、より多くのHTTPリクエストなどを開始できます。問題は、OkHttp(またはAndroidのその他のHTTPライブラリ)で同じパターンを見ることができないことです。したがって、80秒の1秒のリクエストを4秒で実行するにはどうすればよいでしょうか。 ?スレッドベースではありませんよね?80(または20)スレッドを起動していませんか?

具体的には、 com.squareup.okhttp.Call.beginRequest() で、sendRequestgetResponseの呼び出しの間にスレッドが生成されないことがわかります。

if (canceled) return null;

try {
    engine.sendRequest();

    if (request.body() != null) {
        BufferedSink sink = engine.getBufferedRequestBody();
        request.body().writeTo(sink);
    }

    engine.readResponse();
} catch (IOException e) {
    HttpEngine retryEngine = engine.recover(e, null);
    if (retryEngine != null) {
        engine = retryEngine;
        continue;
    }

    // Give up; recovery is not possible.
    throw e;
}

Response response = engine.getResponse();

では、80の "並列"呼び出しをどのようにして可能にするのでしょうか。

ライブラリを使用するためにこれを知る必要はありませんが、非同期プログラミングに興味があり、OkHttp/SquareIncがどのように動作するかを本当に理解したいこれを解決しました。

18
Nilzor

OkHttpソースをプロジェクトにリンクし、ロギングをコアリクエストクラスであるCall.Javaに挿入することで、いくつかのテストを行いました。私が見つけたのは、OkHttpが実際に各呼び出しにスレッドを使用し、誤って想定したように応答を待機している間にnotしないことです。たとえばVolleyよりも高速である唯一の理由は、OkHttpがInteger.MAX_VALUEDipatcher.Java行58)を使用しているのに対して、Volleyはスレッド制限を4にハードコーディングしているためです。

executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
      new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));

以下は、80件のリクエストを「非同期に」キューに入れて実行したときのLogCatログの抜粋です。

05-31 12:15:23.884  27989-28025/nilzor.okhttp I/OKH﹕ Starting request 1
05-31 12:15:23.884  27989-28026/nilzor.okhttp I/OKH﹕ Starting request 2
05-31 12:15:24.044  27989-28199/nilzor.okhttp I/OKH﹕ Starting request 79
05-31 12:15:24.054  27989-28202/nilzor.okhttp I/OKH﹕ Starting request 80
05-31 12:15:25.324  27989-28025/nilzor.okhttp I/OKH﹕ Getting response 1 after 1436ms
05-31 12:15:26.374  27989-28026/nilzor.okhttp I/OKH﹕ Getting response 2 after 2451ms
05-31 12:15:27.334  27989-28199/nilzor.okhttp I/OKH﹕ Getting response 79 after 3289ms
05-31 12:15:26.354  27989-28202/nilzor.okhttp I/OKH﹕ Getting response 80 after 2305ms

形式xxxxx-yyyyyの3番目の列は、プロセスID(x)とスレッドID(y)を示します。各リクエストが独自のスレッドを取得する方法と、同じスレッドが応答を処理する方法に注意してください。 完全ログ 。これは、応答の待機中に80のブロッキングスレッドがあることを意味します。これは、true非同期プログラミングを行う方法ではありません。

OkHttp/Square Incの防御では、真のエンドツーエンドの非同期HTTP通信があるとは決して主張せず、消費者に非同期インターフェースを提供するだけです。それは結構です。そしてそれはまたよく機能し、他の多くのことを行います。これは良いライブラリですが、真の非同期HTTP通信があると誤解しました。

それ以来、私が探しているものを見つけるためにキーワード「NIO」を探すことを理解しました。 AndroidAsyncIon のようなライブラリは有望なようです。

21
Nilzor

現在、OkHttpは非同期ソケットを使用していません。 enqueue()で非同期APIを使用している場合、Dispatcherは複数のスレッドを起動し、複数の同時リクエストを作成します。すべてのリクエストに同じOkHttpクライアントを使用すると、ホストごとに5つの接続に制限されます。

14
Jesse Wilson