微服務架構~Zuul1.0和2.0咱們該如何選擇?

介紹

在今年5月中,Netflix終於開源了它的支持異步調用模式的Zuul網關2.0版本,真可謂千呼萬喚始出來。從Netflix的官方博文[附錄1]中,咱們得到的信息也比較使人振奮:前端

The Cloud Gateway team at Netflix runs and operates more than 80 clusters of Zuul 2, sending traffic to about 100 (and growing) backend service clusters which amounts to more than 1 million requests per second. Netflix部署了超過80+的Zuul2雲網關集羣,流量通過Zuul2集羣被路由到後端超過100+的微服務,且每秒鐘通過Zuul2集羣的請求超過100萬。git

Zuul2看起來很強大,支持異步高併發(Zuul1僅支持同步)特性看起來很亮眼,那麼咱們是否就應該拋棄Zuul1,開始擁抱Zuul2呢?做爲架構師,咱們不能盲目追時髦,技術的選擇必須基於實踐和理性的分析,基於我以前對Zuul1的一線落地實戰經驗,也基於我近期對Zuul2的一些調研,我會在本文中對Zuul1和Zuul2作一個比較客觀的編程模型和優劣分析,同時給出個人我的建議。github

Zuul 1.0編程模型和優劣

 

 

Zuul1設計比較簡單,代碼很少也比較容易讀懂,它本質上就是一個同步Servlet,採用多線程阻塞模型,如上圖所示[圖片來自附錄4]。編程

同步Servlet使用thread per connection方式處理請求。簡單講,每來一個請求,Servlet容器要爲該請求分配一個線程專門負責處理這個請求,直到響應返回客戶端這個線程纔會被釋放返回容器線程池。若是後臺服務調用比較耗時,那麼這個線程就會被阻塞,阻塞期間線程資源被佔用,不能幹其它事情。咱們知道Servlet容器線程池的大小是有限制的,當前端請求量大,然後臺慢服務比較多時,很容易耗盡容器線程池內的線程,形成容器沒法接受新的請求,Netflix爲此還專門研發了Hystrix[附錄2]熔斷組件來解決慢服務耗盡資源問題。後端

注意在上圖Netflix給出的場景中,它的後臺服務調用也是啓動另一個IO線程來處理的,可是本質上仍是阻塞模式,後臺IO線程在處理的時候,前臺容器線程仍然是阻塞的。安全

同步阻塞模式有利有弊,分析以下圖:多線程

 

 

優點

同步阻塞模式的編程模型比較簡單,整個請求->處理->響應的流程(call flow)都是在一個線程中處理的,這樣開發調試比較方便易於理解,好比出了問題跟蹤調試比較方便。另外,線程局部變量(ThreadLocal)機制在同步多線程模式下能夠工做,有些監控產品,例如CAT調用鏈依賴於ThreadLocal,在同步多線程模式下,CAT埋點比較方便,調用鏈關係的展現也比較直觀。架構

不足

咱們知道線程自己須要消耗CPU和內存資源,且多線程之間切換是有開銷的(所謂的上下文切換Context Switch開銷),線程越多,這種上下文切換的開銷就越大,同步阻塞模式通常會啓動不少的線程,必然引入線程切換開銷。另外,同步阻塞模式下,容器線程池的數量通常是固定的,形成對鏈接數有必定限制,當後臺服務慢,容器線程池易被耗盡,一旦耗盡容器會拒絕新的請求,這個時候容器線程其實並不忙,只是被後臺服務調用IO阻塞,可是幹不了其它事情。併發

整體上,同步阻塞模式比較適用於計算密集型(CPU bound)應用場景。對於IO密集型場景(IO bound),同步阻塞模式會白白消耗不少線程資源,它們都在等待IO的阻塞狀態,沒有作實質性工做。運維

Zuul 2.0編程模型和優劣

 

 

Zuul2的設計相對比較複雜,代碼也不太容易讀懂,它採用了Netty實現異步非阻塞編程模型,如上圖所示[圖片來自附錄4]。

通常異步模式的本質都是使用隊列Queue(或稱總線Bus),在上圖中,你能夠簡單理解爲前端有一個隊列專門負責處理用戶請求,後端有個隊列專門負責處理後臺服務調用,中間有個事件環線程(Event Loop Thread),它同時監聽先後兩個隊列上的事件,有事件就觸發回調函數處理事件。這種模式下須要的線程比較少,基本上每一個CPU核上只須要一個事件環處理線程,前端的鏈接數能夠不少,鏈接來了只須要進隊列,不須要啓動線程,事件環線程由事件觸發,沒有多線程阻塞問題。

異步非阻塞模式也是有利有弊,分析以下圖:

 

 

優點

異步非阻塞模式啓動的線程不多,基本上一個CPU core上只需啓一個事件環處理線程,它使用的線程資源就不多,上下文切換(Context Switch)開銷也少。非阻塞模式能夠接受的鏈接數大大增長,能夠簡單理解爲請求來了只須要進隊列,這個隊列的容量能夠設得很大,只要不超時,隊列中的請求都會被依次處理。

不足

異步模式讓編程模型變得複雜。一方面Zuul2自己的代碼要比Zuul1複雜不少,Zuul1的代碼比較容易看懂,Zuul2的代碼看起來就比較費勁。另外一方面異步模型沒有一個明確清晰的請求->處理->響應執行流程(call flow),它的流程是經過事件觸發的,請求處理的流程隨時可能被切換斷開,內部實現要經過一些關聯id機制才能把整個執行流再串聯起來,這就給開發調試運維引入了不少複雜性,好比你在IDE裏頭調試異步請求流就很是困難。另外ThreadLocal機制在這種異步模式下就不能簡單工做,由於只有一個事件環線程,不是每一個請求一個線程,也就沒有線程局部的概念,因此對於CAT這種依賴於ThreadLocal才能工做的監控工具,調用鏈埋點就很差搞(實際能夠工做但須要進行特殊處理)。

整體上,異步非阻塞模式比較適用於IO密集型(IO bound)場景,這種場景下系統大部分時間在處理IO,CPU計算比較輕,少許事件環線程就能處理。

Zuul1和Zuul2的性能比對

Netflix自己對網關使用異步非阻塞模式這件事情是很是謹慎的,它們進行了嚴格的性能測試,下面是Netflix給出的一些性能數據,來自Zuul2網關核心研發成員Arthur Gonigberg的ppt(Zuul's Journey to Non-Blocking)[附錄3]:

 

 

Netflix給出了一個比較模糊的數據,大體Zuul2的性能比Zuul1好20%左右,這裏的性能主要指每節點每秒處理的請求數。爲何說模糊呢?由於這個數據受實際測試環境,流量場景模式等衆多因素影響,你很難復現這個測試數據。即使這個20%的性能提高是確實的,其實這個性能提高也並不大,和異步引入的複雜性相比,這20%的提高是否值得是個問題。Netflix自己在其博文[附錄4]和ppt[附錄3]中也是有點含糊其詞,甚至自身都有一些疑問的。

While we did not see a significant efficiency benefit in migrating to async and non-blocking, we did achieve the goals of connection scaling.

比較明確的是,Zuul2在鏈接數方面表現要好於Zuul1,也就是說Zuul2能接受更多的鏈接數。

Zuul 2.0架構和額外特性

 

 

上圖是Zuul2的架構,和Zuul1沒有本質區別,兩點變化:

  1. 前端用Netty Server代替Servlet,目的是支持前端異步。後端用Netty Client代替Http Client,目的是支持後端異步。

  2. 過濾器換了一下名字,用Inbound Filters代替Pre-routing Filters,用Endpoint Filter代替Routing Filter,用Outbound Filters代替Post-routing Filters。

 

 

上圖是Zuul2的一些功能亮點,我我的認爲除了對HTTP/2的支持算是一個亮點,其它都是在安全、彈性和運維層面的一些優化,不能算新功能。其中像Request Passport,Status Categories,Request Attempts這些所謂的新功能,實際上是爲了減輕異步帶來的複雜性,方便開發人員調試異步請求而專門開發的。

建議

基於上述分析,我對你們的建議是在生產環境中繼續使用Zuul1,緣由以下:

  1. Zuul1同步編程模型簡單,門檻低,開發運維方便,容易調試定位問題。Zuul2門檻高,調試不方便。

  2. Zuul1監控埋點容易,好比和調用鏈監控工具CAT集成,若是你用Zuul2的話,CAT很差埋點是個問題。

  3. Zuul1已經開源超過6年,穩定成熟,坑已經被踩平。Zuul2剛開源很新,實際落地案例很少,難說有bug須要踩坑。

  4. 大部分公司達不到Netflix那個量級,Netflix是要應對每日千億級流量,它們才挖空心思搞異步,通常公司億級可能都不到,Zuul1綽綽有餘。

  5. Zuul1能夠集成Hystrix熔斷組件,能夠部分解決後臺服務慢阻塞網關線程的問題。

  6. Zuul1可使用Servlet 3.0規範支持的AsyncServlet進行優化,能夠實現前端異步,支持更多的鏈接數,達到和Zuul2同樣的效果,可是不用引入太多異步複雜性。波波和極客時間合做的課程《微服務架構和實踐160講》,7月份立刻上線第三模塊《微服務網關Zuul架構和實踐》,其中會講解Zuul1如何使用AsyncServlet優化鏈接數,歡迎你們關注。

對於Zuul2,個人建議是持謹慎觀望的態度,能夠在測試環境小規模實驗驗證,可是暫不上到生產環境。

結論

  1. 同步異步各有利弊,同步多線程編程模型簡單,但會有線程開銷和阻塞問題,異步非阻塞模式線程少併發高,可是編程模型變得複雜。

  2. 架構師作技術選型須要嚴謹務實,具有批判性思惟(Critical Thinking),即便是對於一線大公司推出的開源產品,也要批判性看待,不可盲目追新。

  3. 我的建議生產環境繼續使用Zuul1,同步阻塞模式的一些不足,可使用熔斷組件Hystrix和AsyncServlet等技術進行優化。波波和極客時間合做的課程《微服務架構和實踐160講》,7月份立刻上線第三模塊《微服務網關Zuul架構和實踐》,其中會講解對Zuul1的這些優化技術,歡迎你們關注。

附錄

  1.  Open Sourcing Zuul 2 https://medium.com/netflix-techblog/open-sourcing-zuul-2-82ea476cb2b3

     

  2.  Hystrix https://github.com/netflix/hystrix

     

  3.  Zuul's Journey to Non-Blocking https://github.com/strangeloop/StrangeLoop2017/blob/master/slides/ArthurGonigberg-ZuulsJourneyToNonBlocking.pdf

     

  4.  Zuul2:Netflix's Journey to Asynchronous,Non-blocking Systems https://medium.com/netflix-techblog/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c

 

轉載:https://mp.weixin.qq.com/s/WlDCpdh3_ygCCd63Gsth6Q

相關文章
相關標籤/搜索