OkHttp3架構(gòu)分析

在OkHttp3中,其靈活性很大程度上體現(xiàn)在,可以intercept其任意一個環(huán)節(jié),而這個優(yōu)勢便是okhttp3整個請求響應(yīng)架構(gòu)體系的精髓所在:


Okhttp請求流程
  • 在OkHttp3中,每一個請求任務(wù)都封裝為一個Call,其實現(xiàn)為RealCall。
  • 而所有的策略幾乎都可以通過OkHttpClient傳入
  • 所有全局策略與數(shù)據(jù),除了存儲在允許上層訪問的OkHttpClient實例以外,還有一部分是存儲在只允許包可見的Internal.instance中(如連接池、路由黑名單等)
  • OkHttp中用戶可傳入的interceptor分為兩類,一類是全局interceptor,該類interceptor在請求開始之前最早被調(diào)用,另外一類為非網(wǎng)頁請求的networkInterceptor,這類interceptor只有在非網(wǎng)頁請求中會被調(diào)用,并且是在組裝完成請求之后,真正發(fā)起請求之前被調(diào)用(這塊具體可以參看RealCall#getResponseWithInterceptorChain()方法)
  • 整個請求過程通過RealInterceptorChain#proceed來連接,在每個interceptor中調(diào)用下一個interceptor來完成整個請求流程,并且在回到當前interceptor后完成響應(yīng)處理
  • 在異步請求中,我們通過Callback來獲得簡單清晰的請求回調(diào)(onFailure、onResponse)
  • 在OkHttpClient中,我們可以傳入EventListener的工廠方法,為每一個請求創(chuàng)建一個EventListener,來接收非常細的事件回調(diào)
完整interceptor-chain

OkHttp3中的線程池

OkHttp 中的對所有的任務(wù)采用 NamedRunnable,約束每個執(zhí)行單元給出對應(yīng)的業(yè)務(wù)名稱,以便于線程維護。

1.異步請求線程池-OkHttp Dispatcher

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  • 該線程池與Android下的 Executors.newCachedThreadPool() 比較類似;
  • 無任務(wù)上限,自動回收閑置60s的線程,適用于大量耗時較短的任務(wù);
  • 雖然線程池無任務(wù)上限,但是Dispatcher對入口enqueue()進行了把關(guān),最大的異步任務(wù)數(shù)默認是64,同一個主機默認是5,當然這兩個默認值是可以修改的,Dispatcher提供的修改接口;
okHttpClient.dispatcher().setMaxRequests(128);
okHttpClient.dispatcher().setMaxRequestsPerHost(10);
  • 通過兩個雙端隊列來維護準備執(zhí)行的任務(wù)和正在執(zhí)行的任務(wù):Deque<AsyncCall> readyAsyncCalls, Deque<AsyncCall> runningAsyncCalls;

  • 在每個任務(wù)結(jié)束時,都會檢查 readyAsyncCalls 是否有任務(wù),在條件滿足的情況下,按照先進先出的原則將任務(wù)移動到 runningAsyncCalls中,并在線程池中執(zhí)行;

異步請求線程池

2.連接池清理線程池-OkHttp ConnectionPool

/**
* Background threads are used to cleanup expired connections. There will be at most a single
* thread running per connection pool. The thread pool executor permits the pool itself to be
* garbage collected.
*/
private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
    Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
  • 該線程池用來清理長時間閑置的和泄漏的連接;
  • 該線程池本身無任務(wù)上限,線程閑置60s自動回收;
  • 雖然任務(wù)無上限,但其通過 cleanupRunning 標記來控制只有一個線程在運行,當連接池中沒有連接后才會被重新設(shè)置為 false;
void put(RealConnection connection) {
    assert (Thread.holdsLock(this));
    if (!cleanupRunning) {
        cleanupRunning = true;
        executor.execute(cleanupRunnable);
    }
    connections.add(connection);
}
  • 次工作線程會不斷地清理,當清理完一遍后超時連接后,根據(jù)當前連接池中最近的下一個空閑超時連接計算出一個阻塞時間并阻塞,直到連接池中沒有任何連接才結(jié)束,并將 cleanupRunning 設(shè)為 false;

  • 在每次有連接加入連接池時,如果當前沒有清理任務(wù)運行,會加入一個清理任務(wù)到到線程池中執(zhí)行;

void put(RealConnection connection) {
    assert (Thread.holdsLock(this));
    if (!cleanupRunning) {
        cleanupRunning = true;
        executor.execute(cleanupRunnable);
    }
    connections.add(connection);
  }
連接池清理線程池

3. 緩存整理線程池-OkHttp DiskLruCache

// Use a single background thread to evict entries.
Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp DiskLruCache", true));
  • 該線程池用于整理本地請求緩存數(shù)據(jù);
  • 緩存的整理包含: 達到閥值大小的文件,刪除最近最少使用的記錄,在有關(guān)操作達到一定數(shù)量以后對記錄進行重建;
  • 最大運行線程數(shù)1,無需考慮線程安全問題,自動回收閑置60s的線程;

4. HTTP2異步事務(wù)線程池-OkHttp Http2Connection

  • HTTP2采用了多路復用,因此需要維護連接有效性,本線程池就是用于維護相關(guān)的各類HTTP2事務(wù);
  • 線程池本身無任務(wù)上限,自動回收閑置60s的線程;
  • 每一個HTTP2連接都有這么一個線程池存在;

相關(guān)閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容