How to let OkHttpClient.Dispatcher consume okhttp3.Request in LIFO(last in first out) order?

1.3k Views Asked by At

Goal:

Last enqueued okhttp3.Call is executed first


Tried solution: LIFO Executor (not work)

I have found a solution for a LIFO Executor(Executor Service with LIFO ordering), and applied this kind of Executor into OkHttpClient by this way

private val httpClient = OkHttpClient.Builder().dispatcher(
        Dispatcher(ThreadPoolExecutor(..., LifoLinkedBlockingDequeue<Runnable>()))
    ).build()

But, this didn't work.

Analysis

After tracing source code in okhttp3.Dispatcher, I found that:

  1. First, every enqueued Call is added into (private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>()) in Dispatcher.java"
  2. Then, those Calls are moved from readyAsyncCalls to the blocking deque of Executor in FIFO order
  3. Finally, Calls in Executor are executed in LIFO order

In my case, tremendous Calls are produced at the same time, and there are relatively less threads consuming them.
--> Most Calls are queued in Dispatcher instead of in Executor at a moment
--> LIFO Executor doesn't play the effect

Example

| Dispatcher: 1,2,3,4,5 | Executor:    | executing:    | done:          |
| Dispatcher: 2,3,4,5   | Executor:1   | executing:    | done:          |
| Dispatcher: 4,5       | Executor:2,3 | executing:1   | done:          |
| Dispatcher: 5         | Executor:4   | executing:3,2 | done:1         |
| Dispatcher:           | Executor:5   | executing:4   | done:1,3,2     |
| Dispatcher:           | Executor:    | executing:5   | done:1,3,2,4   |
| Dispatcher:           | Executor:    | executing:    | done:1,3,2,4,5 |
  • Calls are moved from Dispatcher to Executor in FIFO order, and this procedure is very "inefficient".
  • Calls from Executor to executing state is in LIFO order. But there are few Calls in Executor, LIFO effect is not obvious.

Does anyone know other way to achieve this?

2

There are 2 best solutions below

0
On

I found out that the reason why it's INEFFICIENT to move requests from Dispatcher to Executor is: there are two maximums for number of moved requests at a time.

// okhttp3.Dispatcher
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
    //do something...    

    if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
    if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
    //do something...
}

Sol

Set a greater value for Dispatcher.maxRequests and Dispatcher.maxRequestPerHost

So that every new incoming request can be moved to executor as soon as possible
--> to give full scope to the characteristic of queue passed into Executor

But... I'm not sure that whether large maximum will cause any side effects...

0
On

Build your own LIFO queue in front of OkHttp’s dispatcher. It accepts calls and holds them until they're ready to be executed. When they are, post them on the dispatcher.