本文來自OPPO互聯網基礎技術團隊,轉載請註名做者。同時歡迎關注咱們的公衆號:OPPO_tech,與你分享OPPO前沿互聯網技術及活動。
隨着公司業務的發展,核心服務流量愈來愈大,使用到的資源也愈來愈多。在微服務架構體系中,大部分的業務是基於Java 語言實現的,受限於Java 的線程實現,一個Java 線程映射到一個kernel 線程,形成了高併發場景下線程資源的極大浪費,線程成爲提升系統併發和吞吐量的瓶頸。java
在微服務架構下,使用同步編程模式時不只形成了資源的極大浪費,而且在流量發生激增波動的時候,受制於系統資源而沒法快速的擴容。本文將探索服務異步化在併發、吞吐量方面對系統帶來的提高。數據庫
首先,以微服務架構中的RPC 服務調用舉例,測試和探索在微服務架構中,異步架構如何提升服務的吞吐量和併發。ESA Stack 是OPPO 自研的基礎框架技術棧,ESA RPC 是自研的RPC 框架。本節測試服務咱們使用ESA RPC 搭建。編程
關於ESA RPC的詳情,能夠參考咱們以前發佈的文章《Dubbo協議解析及ESA RPC實踐》。服務器
下圖所示爲測試環境架構。其中Service A 既是服務端也是客戶端,它模擬了生產環境中大部分服務的角色。咱們對Service A 分別採用同步模型和純異步模型進行壓測,其中純異步模型包含了客戶端、服務端邏輯的異步處理。Service B 模擬一個耗時爲N ms 的下游服務,爲Service A 的調用提供固定的延時響應。架構
測試服務器的配置爲8 核16G,千兆網卡。併發
測試場景1:框架
併發壓測客戶端200~8000,服務耗時50ms,分別對同步和異步架構進行壓測,對比TPS、服務耗時、CPU 上下文切換;同步模式下,線程數和併發客戶端相同;異步模式下,使用框架默認的200 線程。測試數據以下。異步
測試場景2:socket
併發壓測客戶端8000,服務耗時50~500ms,分別對同步和異步模式進行壓測,對比TPS、服務耗時、CPU 上下文切換;同步模式下服務端8000 線程;異步模式下,使用框架默認的200 線程。async
併發指服務瞬時同時處理的任務數(包含處於IO 等待狀態的任務)。服務端設置業務處理線程200,那麼同步模式下能提供的併發爲200;純異步模式下服務併發不受線程限制,IO密集型服務尤爲收益。在系統流量突增的情景下,異步模式具備更強的可擴展性(Scalability)。
根據上面的測試數據能夠作出如下對比:
從而得出如下結論:
結論分析:
在操做系統中,線程是CPU 調度的基本單位;阻塞調用是指發起調用後,線程進入阻
塞狀態(讓出CPU),直到得到結果或異常返回;非阻塞調用是指不等待結果,調用不阻塞線程直接返回。
同步和異步關注的是消息通訊機制;同步就是在發起調用後就獲得返回結果(未必是完整結果),也就是由調用者主動等待結果;異步則是調用在發出以後直接返回,經過信號通知、回調函數處理來通知結果。
非IO 系統調用層面, 阻塞/非阻塞和同步/異步基本是同義詞;在IO 系統調用層面,同步/異步和阻塞/非阻塞有如下組合:
咱們經過一個簡單的客戶端來介紹四種IO 模型的代碼寫法:
同步阻塞IO
非同步阻塞IO
多路複用IO
Asynchnorous IO
對四中IO 模型,有如下的對比:
微服務架構下的全鏈路包含了網關層、WEB 服務、RPC 服務、數據層等。目前公司的網關層已經實現了純異步架構,Web 框架和RPC 框架支持純異步編程,數據存儲層目前異步方案還不成熟。
網關層因爲其特殊性,不須要訪問業務數據庫只作協議轉換和流量轉發,目前已經使用了純異步的架構;其IO 密集型的特色,特別適合純異步的架構,能夠極大的節省資源。
Web 服務做爲微服務體系內的重要組成,服務節點衆多,傳統的Web 服務框架SpringMVC 不支持純異步化編程,OPPO 自研Web 框架Restlight 支持純異步編程,且性能遠超SpringMVC。下面是性能對比及Restlight 異步實踐。
Restlight 框架異步編程實踐:經過Controller 方法返回值區分同步和異步調用,且支持三種異步調用方式,CompletableFuture、ListenableFuture(Guava)、Future(Netty)。
RPC 調用等待下游response 返回時,線程不該處於block 狀態;做爲微服務架構中數據流量最大的一部分,RPC 調用異步化的收益巨大;目前ESA RPC 已經具有了純異步化的能力,提供RPC 調用的服務通常既是客戶端也是服務端,所以包含了客戶端異步調用能力和服務端異步處理能力;爲了兼容存量接口,ESA RPC 既支持CompletableFuture 也支持普通返回值的接口。
客戶端異步化實踐:底層使用異步非阻塞IO 收發網路數據包,使用CompletableFUture傳遞IO 事件以實現響應式編程,客戶端不被RPC 調用阻塞,可繼續調用其餘服務。
接口返回CompletableFuture 來實現異步調用:
普通接口使用ESARpcContext::asyncCall 實現異步調用:
服務端異步化實踐:經過服務端異步功能返回CompletableFuture 給框架以釋放Biz 線程,自定義線程池或者IO 線程池收到下游response 後,完成返回給框架的Future。
接口定義返回CompletableFuture 來實現異步調用:
普通接口經過ESARpcContext::startAsync 開啓服務端異步:
數據操做是每一個請求調用鏈的終點,純異步的架構必須使用異步存儲層客戶端,目前OPPO 沒有自研的存儲層異步客戶端,但業界開源方案欣欣向榮:
異步調用目的在於防止當前業務線程被阻塞。僞異步將任務包裝爲Runnable 放入另外一個線程執行並等待,當前Biz 線程不阻塞;純異步爲響應式編程模型,經過IO 實踐驅動任務完成。他們的區別不在因而否將請求放入另外一個線程池執行,而在因而否有線程阻塞等待Response。
相比於同步模型,異步模型存在如下問題:
簡單來講,異步編程就是以編程的簡單性(simplity)來交換性能(performance)。
目前在其餘語言中,Erlang、Go、Kotlin 等都支持了協程,使用攜程的好處是在語言層面支持了異步調用,業務代碼可使用同步的寫法達到異步的效果,線程不被阻塞,避免大量的CPU 上下文切換,提高系統的性能。
目前Java 對協程的支持也在進行中, Project Loom 就是Java 的協程項目:http://openjdk.java.net/proje...。
主要有如下幾個概念:
開發者可使用Fiber 來執行業務代碼塊,當遇到LockSupport::park、socket io 等阻塞調用時,Fiber 中的代碼單元執行會被阻塞,可是底層的線程並不會被阻塞。由此達到了開發同步模式代碼,運行時達到異步執行的目的。
將來,ESAStack服務框架會支持協程。目前 Restlight框架已經支持協程並在內部開始試用,ESARPC也有支持協程的計劃。框架提供的服務線程使用 Fiber 執行業務邏輯,業務實現中數據庫請求、下游服務調用均在 Fiber 之中執行, 其包含的 IO 等阻塞調用只掛起 Fiber 而不阻塞所在線程,從而避免了過多的上下文切換提高 了吞吐量,達到了和異步模式同樣的效果。