OkHttp3 任務隊列

來源:互聯網
上載者:User

標籤:scm   pmc   mpm   dom   msm   包括   條件   ase   plm   

OkHttp3 有兩種運行方式:

1.同步阻塞調用並且直接返回;

2.通過內部線程池分發調度實現非阻塞的非同步回調;

 

下面講的是非阻塞非同步回調,OkHttp在多並髮網絡下的分發調度過程,主要是Dispatcher對象:

 

 多線程:多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。但如果對多線程應用不當,會增加對單個任務的處理時間

 ThreadPool線程池:線程池的關鍵在於線程複用以減少非核心任務的損耗。

比如:

T = T1+T2+T3其中T1和T3是多線程本身的帶來的開銷(在Java中,通過映射pThead,並進一步通過SystemCall實現native線程),我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者並沒有注意到這一點,所以在程式中頻繁的建立或銷毀線程,這導致T1和T3在T中佔有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優點(並發性)。

線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。

 

1.通過對線程進行緩衝,減少了建立銷毀的時間損失;

2.通過控制線程數量閥值,減少了當線程過少時帶來的CPU閑置(比如說長時間卡在I/O上了)與線程過多時對JVM的記憶體與線程切換時系統調用的壓力.

 

構造單例線程池:

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;
}
參數說明:
  • int corePoolSize: 最小並發線程數,這裡並發同時包括空閑與活動的線程,如果是0的話,空閑一段時間後所有線程將全部被銷毀。
  • int maximumPoolSize: 最大線程數,當任務進來時可以擴充的線程最大值,當大於了這個值就會根據丟棄處理機制來處理
  • long keepAliveTime: 當線程數大於corePoolSize時,多餘的空閑線程的最大存活時間,類似於HTTP中的Keep-alive
  • TimeUnit unit: 時間單位,一般用秒
  • BlockingQueue<Runnable> workQueue: 工作隊列,先進先出,可以看出並不像Picasso那樣設定優先隊列。(阻塞隊列)
  • ThreadFactory threadFactory: 單個線程的工廠,可以打Log,設定Daemon(即當JVM退出時,線程自動結束)等。
可以看出,在Okhttp中,構建了一個閥值為[0, Integer.MAX_VALUE]的線程池,它不保留任何最小線程數,隨時建立更多的線程數,當線程空閑時只能活60秒,它使用了一個不儲存元素的阻塞工作隊列,一個叫做"OkHttp Dispatcher"的線程工廠。也就是說,在實際運行中,當收到10個並發請求時,線程池會建立十個線程,當工作完成後,線程池會在60s後相繼關閉所有線程。 反向 Proxy模型:

  在OkHttp中,使用了與Nginx類似的反向 Proxy與分發技術,這是典型的單生產者多消費者問題。我們知道在Nginx中,使用者通過HTTP(Socket)訪問前置的伺服器,伺服器會添加Header並自動轉寄請求給後端叢集,接著返回資料結果給使用者(比如簡書上次掛了也顯示了Nginx報錯)。通過將工作分配給多個後台伺服器並共用Session,可以提高服務的負載平衡能力,實現非阻塞、高可用、高並發串連,避免資源全部放到一台伺服器而帶來的負載,速度,線上率等影響。

而在OkHttp中,非常類似於上述情境,它使用Dispatcher作為任務的派發器,線程池對應多台後置伺服器,用AsyncCall對應Socket請求,用Deque<readyAsyncCalls>對應Nginx的內部緩衝

具體成員如下

  • maxRequests = 64: 最大並發請求數為64
  • maxRequestsPerHost = 5: 每個主機最大請求數為5
  • Dispatcher: 分發者,也就是生產者(預設在主線程)
  • AsyncCall: 隊列中需要處理的Runnable(封裝了非同步回調介面)
  • ExecutorService:消費者池(也就是線程池)
  • Deque<readyAsyncCalls>:緩衝(用數組實現,可自動擴容,無大小限制)
  • Deque<runningAsyncCalls>:正在啟動並執行任務,僅僅是用來引用正在啟動並執行任務以判斷並發量,注意它並不是消費者緩衝

通過將請求任務分發給多個線程,可以顯著的減少I/O等待時間

OkHttp的任務調度當我們希望使用OkHttp的非同步請求時,一般進行如下構造:當HttpClient的請求入隊時,根據代碼,我們可以發現實際上是Dispatcher進行了入隊操作
synchronized void enqueue(AsyncCall call) {
 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加正在啟動並執行請求
 runningAsyncCalls.add(call);
 //線程池執行請求
  executorService().execute(call);
} else {
//添加到緩衝隊列排隊等待
readyAsyncCalls.add(call);
}
}
可以發現請求是否進入緩衝的條件如下:(runningRequests<64 && runningRequestsPerHost<5)如果滿足條件,那麼就直接把AsyncCall直接加到runningCalls的隊列中,並線上程池中執行(線程池會根據當前負載自動建立,銷毀,緩衝相應的線程)。反之就放入readyAsyncCalls進行緩衝等待。

我們再分析請求元素AsyncCall(它實現了Runnable介面),它內部實現的execute方法如下:

當任務執行完成後,無論是否有異常,finally程式碼片段總會被執行,也就是會調用Dispatcher的finished函數,開啟源碼,發現它將正在啟動並執行任務Call從隊列runningAsyncCalls中移除後,接著執行promoteCalls()函數這樣,就主動的把緩衝隊列向前走了一步,而沒有使用互斥鎖等複雜編碼.

通過上述的分析,我們知道了:

  1. OkHttp採用Dispatcher技術,類似於Nginx,與線程池配合實現了高並發,低阻塞的運行
  2. Okhttp採用Deque作為緩衝,按照入隊的順序先進先出
  3. OkHttp最出彩的地方就是在try/finally中調用了finished函數,可以主動控制等待隊列的移動,而不是採用鎖或者wait/notify,極大減少了編碼複雜性
地址:
http://www.jianshu.com/p/aad5aacd79bf

 






 


 

 

 

 

 

 

 

 

 


OkHttp3 任務隊列

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.