TARS爲SpringCloud提供高性能的RPC能力

傳統HTTP存在的瓶頸

Spring Cloud 是一個優秀的開源微服務解決方案,一般採用 HTTP + json 的 REST 接口對外提供服務,簡潔易用部署方便,不少公司也基於 Spring Cloud 做爲基礎架構去構建自身的微服務架構。可是隨着業務規模和用戶規模的增加,傳統基於的 HTTP 的服務會逐步暴露出一些問題。前端

首先是性能的問題,隨着用戶請求量的增加和業務邏輯複雜度的提高,咱們會發現微服務的單機性能會成爲系統瓶頸。git

其次是穩定性問題,當一個服務節點A須要依賴於後端的幾個服務的時候,咱們會發現當其中一個被依賴的服務發生卡頓,極可能會致使前端的服務節點A產生毛刺甚至沒法繼續提供服務,並且當問題節點沒有可以被及時屏蔽或者恢復的時候,還有可能會致使整個系統雪崩。github

TARS如何爲SpringCloud提供高性能解決方案

TARS 是騰訊從2008年到今天一直在使用的後臺邏輯層的統一應用框架,上述問題在 TARS 框架的發展過程當中已經獲得了比較好的解決。如今,TARS 經過插件集成到 Spring Cloud 體系中,但願經過輸出 TARS 的 RPC 能力針對某些對性能和穩定性要求更高應用的場景提供一種新的解決方案,而且提供了基於 Spring Boot 的開發方式,符合Spring Cloud開發者的使用習慣,能夠僅使用較小的開發成本在整個Spring Cloud體系中引入TARS的RPC能力。編程

將 TARS 結合到 Spring Cloud 中使用,經過 TARS 提供的長鏈接異步調用和二進制協議能夠明顯的提高 RPC 調用性能。長鏈接經過鏈接複用減小總體的鏈接數量減小了資源消耗,同時經過二進制協議提高了編解碼效率提高了總體的 RPC 性能。json

一. 解決SpringCloud傳統HTTP網絡鏈接使用率不高的問題
問題:
因爲HTTP協議自己是無狀態的,因此發起一次請求的時候必須等待上一個請求響應才能再次使用這個鏈接,就算是採用流水線模式一個鏈接上的請求也會被以前發起的請求所阻塞,若是要提升併發能力則必需要創建大量的鏈接,而鏈接的創建、維持和銷燬都會消耗系統資源。後端

解決:
由於HTTP協議的特性,HTTP的回包是依賴於請求的前後順序的,必需要按照順序處理完一個請求再處理下一個請求,若是但願並行的處理請求則只能經過創建新的連接從而產生建連的時間開銷以及維護鏈接須要的CPU和內存資源。緩存

而TARS的協議設計是TARS的私有協議,每一個請求會帶有一個請求id,經過同一個連接來發送多個請求能夠經過id來匹配返回從而避免了線程阻塞,從而下降了硬件資源消耗。網絡

圖片描述

經過上圖能夠看到,TARS能夠在同一個鏈接上不斷的寫入請求和接收響應,而客戶端經過請求Id來關聯每個請求和對應的響應,從而能夠複用鏈接,避免了資源的浪費,一般狀況下一個客戶端和一個服務端之間僅使用數個鏈接就能夠知足傳輸的要求。架構

二. 解決SpringCloud傳統HTTP通信協議性能低下的問題
問題:
HTTP + json 自己是一種可讀性很高的文本協議,所以實際傳輸的數據包會比二進制協議要大很多,並且文本協議在數據的序列化反序列化效率上相比二進制數據的效率要低不少,因此 HTTP 協議自己的性能就不高。併發

解決:
TARS的數據傳輸採用的是TARS協議進行編解碼,TARS協議是一種二進制協議,相較於常見的JSON等文本協議,二進制協議主要有兩個方面的優點:

  1. 編解碼效率
    二進制協議的編解碼是按二進制位直接進行編解碼的,減小了對不肯定的字符串解析的過程,直接從對應的二進制位讀取數據,效率相比解析文本協議有很是大的提高。
  2. 網絡包大小

由於全部的數據都是採用二進制存儲,數據按位存儲減小了對空間的浪費,使得數據序列化後能減小對空間的佔用。

TARS協議採用.tars文件定義接口和數據接口,經過提供的工具能夠將數據和接口定義翻譯成各類語言的代碼實現。

接口的共享只須要提供接口的定義文件,使用者經過定義文件直接生成客戶端接口代碼便可。這樣減小了雙方的溝通成本,避免了須要寫大量的接口定義文檔以及解析JSON所需的對象。

三. 解決SpringCloud傳統 HTTP服務基於同步線程模型的性能問題
問題:
傳統的 HTTP 服務可能是基於同步的線程模型,因爲 HTTP 協議自己無狀態,因此在協議層面就不支持異步,因此當咱們在客戶端發起一次 HTTP 調用時主調線程必須掛起等待被調響應請求,這個時候主調線程的資源則被浪費了,由於線程資源是有限的,大量線程被掛起等待白白浪費了主調方的運算資源。

解決:
相比於使用HTTP協議的常規方案,TARS首先提供的特性就是異步長鏈接的RPC調用方式:

發起一個異步調用以後,當前線程並不會被阻塞而是繼續執行,當收到服務端響應以後在回調線程池中經過回掉函數來執行結果的處理。這樣全部的處理線程都一直處於工做的狀態中,而不會掛起致使線程資源的浪費。總體上提高了服務的處理能力。

TARS的異步能力主要是經過兩個部分的異步來實現的,首先是網絡首發包的異步,TARS的網絡層實現採用了Reactor模型,經過nio提供的事件IO實現基於事件的異步網絡IO。第二是線程模型的異步,咱們從線程模型上來看TARS如何是作到異步調用的:

圖片描述

TARS的主要經過上圖的過程來完成異步調用,首先主調線程發起異步調用,主調線程將請求內容加入網絡線程池的發送隊列中,以後該線程繼續執行。網絡線程池使用Reactor模型實現,經過nio提供的Selecter實現事件IO,因此全部網絡線程均是事件驅動的異步IO,當監聽到對應鏈接的寫事件後將請求發送,等待監聽到讀事件後讀取響應並交給回調線程處理響應。這樣全部的線程都避免了IO阻塞達到了更高的利用效率。

四. 解決SpringCloud服務端基於同步線程模型的穩定性問題
問題:
微服務的服務端基於同步的線程模型面臨的最大的隱患就是線程的IO等待,好比說一個基於同步的線程模型的微服務,依賴後面的3個接口,微服務自己的線程數是50個,那麼當後面依賴的3個接口中有一個延時飈高,只須要有50個對問題接口的調用,就足夠把整個微服務的進程掛起,由於當前已經沒有線程可以對外提供服務了(固然能夠設置超時,可是設置超時治標不治本)。同時,因爲微服務線程對問題接口的IO等待,會致使微服務的隊列中堆積大量的等待時間過長(可能已經超時)的請求,當問題接口恢復後,服務端會消耗資源去處理大量的過時的請求(請求超時,客戶端再也不等待)致使問題進一步惡化,嚴重的甚至會致使系統雪崩。

解決:
TARS提供了純異步化編程,和服務端過載保護的能力,在服務端保證收到大量的請求也可以保證服務的正常處理效率,其次由於主調方採用長鏈接和異步調用,避免了大量新建鏈接和阻塞帶來的資源浪費從而提高了服務的總體穩定性。

此外,若是服務端收到過量的請求每每會致使服務端的線程競爭,讓服務端的處理能力低於正常的處理水平,在TARS則經過隊列來進行過載保護。咱們來看TARS的線程模型:

圖片描述

在網絡線程收到請求後,TARS會將請求先加入請求隊列,工做線程從請求隊列中獲取請求進行處理,若是短期內大量請求到達只會被緩存到請求隊列中並不會影響工做線程池的處理能力。若是工做線程池從隊列中取到請求發現其已經超時則會直接丟棄請求避免處理無效的請求。

經過上面TARS的解決方案,看看實際的使用場景

場景一.同步調用,性能提高一倍標題
一般能夠簡單能夠簡單的改造服務,將原本的HTTP接口改成使用TARS,咱們對比一下在同步調用場景下TARS調用和HTTP調用的性能差別,這裏規定了服務端線程數爲100個線程,服務端的處理都爲簡單的echo服務。咱們對比一下在同一臺機器上不一樣RPC方式、不一樣的客戶端線程數以及不一樣數據包大小的TPS數據:

圖片描述

能夠看出,由於採用了鏈接複用和二進制的協議,總體的調用效率相比使用HTTP有了很是明顯的提高,並且是僅僅在簡單優化了一下調用方式的狀況下,對業務處理邏輯並無影響。

場景二.異步調用,避免阻塞,提高性能
假如咱們在Spring Cloud中存在這樣一個調用關係,A服務須要調用B服務,而B服務須要依賴處理耗時遠大因而B服務的C服務。好比在一般的業務場景中,若是API接口須要調用一個訂單生成的服務,而訂單生成服務只須要生成訂單ID計算量相對較小,可是他還須要依賴一個訂單寫入服務,應爲涉及到庫存修改、訂單寫入須要一系列的事務處理,總體耗時遠遠大於訂單ID的生成,因此訂單服務大量的線程資源浪費在了等待訂單寫入服務上。在這種狀況下可使用TARS改造訂單服務和寫入服務,從而使用異步調用寫入服務來提高資源利用率,採用TARS提供的異步RPC能力來進行跟深度的改造:

圖片描述

一般狀況下若是使用同步調用,由於B須要等待C服務的響應,須要花上自身處理耗時的數倍來進行等待C服務返回結果,線程被阻塞浪費線程資源。這樣的狀況咱們能夠保持A服務不變,提供REST接口,而B服務採用異步調用來進行改造。以下圖所示:

圖片描述

咱們經過簡單的代碼來模擬上述過程,加入C服務的邏輯時收到請求後Sleep 10s後返回結果,C服務有10個處理線程,最開始B和C之間採用同步調用,要達到最大的併發效率B服務必須也提供10個線程纔可以達到最大的併發效率 TPS 爲1。此時咱們採用異步調用改造B服務:

圖片描述

此時僅需核數 + 1個線程便可達到最大的處理效率。在一般的業務使用中,若是全部IO均用異步實現,那麼只使用核數+1個線程便能達到較高的處理效率,從而避免了同步IO帶來的資源浪費。

對上述狀況進行測試,咱們規定C服務默認採用100個線程,服務的處理過程爲Sleep 10s,用以模擬一個耗時比較高的資源服務。B服務爲一個依賴資源服務C的普通服務,即收到C的結果即返回,在測試中B服務分別採用同步和異步的方式調用C服務,經過調整線程數記錄B服務在不一樣線程數的狀況下能提供的最大吞吐:

圖片描述

由於C服務能提供的最大TPS爲10,能夠看出使用TARS的異步調用由於避免了阻塞,僅使用較少的線程數即可以達到對資源服務C的充分利用,從而避免了對資源的浪費。

在以上改造中,對外的HTTP接口並不須要改動,能夠僅在內部須要提高RPC性能和用到異步調用的地方進行改造便可,能夠平滑的按服務逐步升級。並且由於均採用Spring boot實現,只須要修改接口,其他全部業務代碼仍是使用Spring注入便可。

寫在最後

咱們經過插件實現了TARS對Eureka服務發現的支持,提供了Spring boot starter包和相關的註解,可以經過符合Spring Cloud開發者習慣的開發方式快速開發服務。經過對服務發現和開發方式對Spring Cloud集成可以讓開發者以較小的代價快速的在整個Spring Cloud的環境中快速引入TARS的RPC能力。

歡迎訪問TARS項目的Github地址:https://github.com/Tencent/Tars

試用最新的TARS on SpringCloud !

相關文章
相關標籤/搜索