21

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

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

具体来说,在com.squareup.okhttp.Call.beginRequest()sendRequest中,我看到andgetResponse调用之间没有产生线程:

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 是如何解决这个问题的。

4

2 回答 2

26

我通过将 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));

这是我“异步”排队和执行 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表示进程 id (x) 和线程 id (y)。请注意每个请求如何获得自己的线程以及同一线程如何处理响应。完整的日志。这意味着我们在等待响应时有 80 个阻塞线程,这不是真正的异步编程应该如何完成的。

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

从那以后,我已经理解寻找关键字“NIO”来找到我正在寻找的东西。AndroidAsyncIon之类的库似乎很有希望。

于 2014-05-31T10:32:00.843 回答
17

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

于 2014-05-31T01:29:10.540 回答