24

I've done some performance testing with the OkHttp library 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 why it is so fast. The developers (Square Inc) advertise Connection Pooling and Async calls, both which I believe contribute to the good performance.

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?

To be specific, in com.squareup.okhttp.Call.beginRequest(), I see no yielding of the thread between the sendRequest and getResponse calls:

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?

I don't need to 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.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Nilzor
  • 18,082
  • 22
  • 100
  • 167
  • Are you issuing the requests one after the other or all at once? If it is one after the other then this *must* take >80s. If all at once then it can go arbitrary fast down to 1s. So how many threads are you usign to issue calls? – usr May 31 '14 at 11:38

2 Answers2

28

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 not yield 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.java line 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":

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-yyyyy indicates 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 true async programming should be done.

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.

I have since understood to look for the keywords "NIO" to find what I'm looking for. Libraries like AndroidAsync and Ion seems promising.

Nilzor
  • 18,082
  • 22
  • 100
  • 167
  • 6
    Just worth noting that you shouldn't compare Volley to OkHttp. You should be using Volley on top of OkHttp. – Jake Wharton Aug 19 '14 at 03:16
  • 1
    When you call enqueue, it checks the max total and per host requests. It then adds the request to a queue or executes it directly. See https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/Dispatcher.java#L109 – black Feb 07 '16 at 16:48
  • 1
    also a .net dev new to java & if i hear async immediately assume IO Completion Ports (windows) and WHATEVER / POSIX AIO (linux)? and was wondering what okhttp3's docs mean with "responses are processed on the request thread" (WTF?) and how that worked with the RxJava call adaptor (which takes a RxScheduler). finally found this line https://github.com/square/retrofit/blob/28d350d99430c87b4ada7d1aa9e08c96884cb388/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java#L171 and boom..they sync call in rx, using scheduler in place of okhttp3 dispatcher – Chris DaMour May 19 '16 at 04:10
  • which is not how true async programming should be done. – Scott 混合理论 Aug 13 '20 at 08:17
17

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

Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • If OkHttp limits itself to 5 connections per host, how do you explain that I got 80 replies from a host that always waits 1 second before responding, in 4.7 seconds? – Nilzor May 31 '14 at 09:04
  • 2
    @JesseWilson I guess this 5 requests per host works only if you don't manually set the different value via `setMaxRequestsPerHost()`? – sh0m1 Nov 01 '16 at 11:13
  • Exactly. You can set whatever limits you want. – Jesse Wilson Nov 04 '16 at 03:38
  • 5 connections actually refer to Idle Connections. The library itself doesnt limit the number connections that can be spawned using a single okhttpclient instance https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/ConnectionPool.java#L89 – rocky Sep 08 '17 at 02:27