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

2022-09-02 00:47:33

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

我来自.NET世界,在.NET 4.5中,你有一个真正的异步HTTP库,带有异步GetResponse方法。通过在等待响应时将线程提供给操作系统,您可以释放资源以启动更多HTTP请求或其他东西。问题是我无法看到与OkHttp(或我研究过的任何其他Android HTTP库)相同的模式。那么,我怎样才能在4秒内执行80个1秒的请求呢?它不是基于线程的,对吧?我没有启动 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是如何解决这个问题的。


答案 1

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

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

以下是 LogCat 日志的摘录,当时我“异步”排队并执行了 80 个请求:

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

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

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

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


答案 2

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


推荐