Java OkHttp 如何在不使用线程的情况下通过看似同步的 HTTP 连接执行并行 HTTP 请求?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23955924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 09:23:55  来源:igfitidea点击:

How is OkHttp performing parallell HTTP Requests with seemingly synchronous HTTP connections, without the use of threading?

javamultithreadingasynchronousokhttp

提问by Nilzor

I've done some performance testing with the OkHttplibrary and found it to be great. It did 80 requests to http://httpbin.org/delay/1, which deliberately pauses 1 sec for each request, in 4.7s on my HTC One phone. I've looked at the code and I'm trying to find out whyit is so fast. The developers (Square Inc) advertise Connection Pooling and Async calls, both which I believe contribute to the good performance.

我用OkHttp库做了一些性能测试,发现它很棒。它对http://httpbin.org/delay/1做了 80 个请求,每个请求故意暂停 1 秒,在 4.7 秒内在我的 HTC One 手机上。我查看了代码,并试图找出为什么它如此之快。开发人员(Square Inc)宣传连接池和异步调用,我相信这两者都有助于实现良好的性能。

I come from the .NET world and in .NET 4.5 you have a true async HTTP library with an Async GetResponse-method. By yielding the thread to the OS while waiting for the response, you free up resources to initiate more HTTP requests, or other stuff. The thing is I cannot see the same pattern with OkHttp (or any other HTTP library for Android I've looked into). So how can I still execute 80 1-second-request in 4 seconds? It's not thread-based, right? I'm not firing up 80 (or 20) threads?

我来自 .NET 世界,在 .NET 4.5 中你有一个真正的异步 HTTP 库和一个 Async GetResponse-method。通过在等待响应时将线程交给操作系统,您可以释放资源来发起更多 HTTP 请求或其他内容。问题是我看不到与 OkHttp(或我研究过的任何其他适用于 Android 的 HTTP 库)相同的模式。那么我怎样才能在 4 秒内执行 80 个 1-second-request 呢?它不是基于线程的,对吧?我没有启动 80(或 20)个线程?

To be specific, in com.squareup.okhttp.Call.beginRequest(), I see no yielding of the thread between the sendRequestand getResponsecalls:

具体来说,在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();

So again, how is making 80 "parallell" calls possible?

那么,如何使 80 个“并行”调用成为可能?

I don't needto know this in order to use the library, but async programming interests me and I'd really like to understand how OkHttp/SquareInc has solved this.

不需要知道这一点就可以使用这个库,但是异步编程让我很感兴趣,我真的很想了解 OkHttp/SquareInc 是如何解决这个问题的。

采纳答案by Nilzor

I did some testing myself by linking the OkHttp source into my project and injecting logging into the core request class - Call.java. What I found was that OkHttp indeed uses a thread for each call and does notyield while waiting for the response as I wrongly assumed. The only reason it is faster than for instance Volley, is that Volley has hard-coded a thread limit of 4, while OkHttp uses Integer.MAX_VALUE(Dipatcher.javaline 58):

我通过将 OkHttp 源链接到我的项目并将日志记录注入核心请求类 - Call.java 进行了一些测试。我发现 OkHttp 确实为每个调用使用了一个线程,并且在等待响应时不会像我错误地假设的那样让步。它比例如 Volley 更快的唯一原因是 Volley 将线程限制硬编码为 4,而 OkHttp 使用Integer.MAX_VALUEDipatcher.java第 58 行):

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

Here is an exerpt of the LogCat log when I queued and excecuted 80 requests "asynchronously":

这是我排队并“异步”执行 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

The third column on format xxxxx-yyyyyindicates process id (x) and thread id (y). Notice how each requests gets its own thread and how the same thread handles the response. Full log. This means that we have 80 blocking threads while waiting for responses, which is not how trueasync programming should be done.

格式的第三列xxxxx-yyyyy表示进程 id (x) 和线程 id (y)。注意每个请求如何获得自己的线程以及同一个线程如何处理响应。完整日志。这意味着我们在等待响应时有 80 个阻塞线程,这不是真正的异步编程应该如何完成。

In OkHttp / Square Inc's defense, they never claim to have true end-to-end async HTTP communication, they only provide an async interface to the consumer. Which is fine. And it also performs well and does a bunch of other stuff. It is a good library, but I misinterpreted it for having true async HTTP communication.

在 OkHttp / Square Inc 的辩护中,他们从未声称拥有真正的端到端异步 HTTP 通信,他们只向消费者提供异步接口。这很好。它也表现良好,并做了很多其他的事情。这是一个很好的库,但我误解了它具有真正的异步 HTTP 通信。

I have since understood to look for the keywords "NIO" to find what I'm looking for. Libraries like AndroidAsyncand Ionseems promising.

从那以后,我明白了寻找关键字“NIO”来找到我要找的东西。像AndroidAsyncIon这样的库看起来很有前途。

回答by Jesse Wilson

OkHttp doesn't use async sockets at this time. If you're using the async API with enqueue(), the Dispatcherwill spin up multiple threads and make multiple concurrent requests. If you use the same OkHttpclient for all requests it will limit itself to 5 connections per host.

OkHttp 目前不使用异步套接字。如果您将异步 API 与 一起使用enqueue()Dispatcher它将启动多个线程并发出多个并发请求。如果您OkHttp对所有请求使用相同的客户端,它会将自身限制为每台主机 5 个连接。