Spring Cloud中五花八門的分散式元件我到底該怎麼學

雙子孤狼發表於2021-12-05

分散式架構的演進

在軟體行業,一個應用服務隨著功能越來越複雜,使用者量越來越大,尤其是網際網路行業流量爆發式的增長,導致我們需要不斷的重構應用的結構來支撐龐大的使用者量,最終從一個簡單的系統主鍵演變成了一個非常複雜的可以支撐高併發的高可用的分散式架構,但是一個系統再複雜也是不斷演變來的,所以從另一方面來說,其實是業務(問題)推動了技術的發展。

傳統的單體應用

在早期,我們開發的都是單體應用,也就是一個系統所有的模組都在一個服務上:

這種傳統的應用開發和運維都非常簡單,隨著使用者量的增加,我們發現應用程式的壓力越來越大,於是我們會選擇對應用進行叢集部署:

當然因為選擇了叢集,我們就需要考慮服務分發的問題,所以需要有負載均衡伺服器,比如我們最常用的 nginx,還有 lvsHaProxy 等,硬體層面也可以選擇 F5 來實現負載均衡等等。

當然,在使用了叢集之後,我們還需要考慮 session 共享的問題,所以相比較單機架構會稍微複雜一點點,那麼到這裡我們應用進行了擴充套件了,這時候發現資料庫又到瓶頸了,所以資料庫又需要進行擴充套件。

資料庫的擴充套件可以有兩種主流方式:

  • 讀寫分離

通過讀寫分離以及在某些場景用分散式儲存系統替換關係型資料庫的方式,能夠降低主庫的壓力,解決資料儲存方面的問題,不過隨著業務的發展,主庫依然會遇到瓶頸。

  • 分庫分表

當採用讀寫分離之後,如果再次遇到瓶頸,那麼就可以採用垂直拆分的方式來實現,垂直拆分的意思是把資料庫中不同的業務資料拆分到不同的資料庫中。但是這樣有些熱門模組依然遲早會遇到瓶頸,於是可以更進一步採用水平拆分,水平拆分就是把同一個表的資料拆分到不同的資料庫中。

垂直拆分還比較容易處理,畢竟同一個模組的資料還是在一起,水平拆分就會比較複雜了,比如說使用者表拆成了兩張,存在不同的資料庫中,那麼存的時候到底該存的哪個庫,取的時候又該到哪個庫去查詢,所以水平拆分需要考慮以下問題:

  • 插入和查詢的路由問題,需要根據某一個條件來決定當前資料應該分到哪個庫。
  • 主鍵的處理,主鍵不能採用自增主鍵的形式,因為不同的庫採用自增主鍵會有衝突。
  • 如果某些查詢需要到兩個庫去查詢,會比較難處理。

資料庫的拆分可以使用當前比較流行的 Sharding JDBC 或者 MyCat 來實現,這時候的架構大致就會如下圖所示:

當然,為了進一步優化,可以視情況加入快取層,或者使用訊息佇列等技術來削峰等優化措施。

分散式架構

分散式架構是指位於網路計算機上的各個元件(系統)僅通過傳遞訊息來通訊和協調目標系統,分散式系統其實也可以認為是一種去中心化的實現思路,對於使用者來說是無感知的。

分散式架構的意義

從單機單使用者到單機多使用者,再到現在的網路時代,應用系統發生了很多的變化,為什麼單體架構會逐漸滿足不了需求轉而要採用分散式架構呢?原因主要有以下幾點:

  1. 升級單機處理能力的價效比越來越低。
  2. 單機處理能力存在瓶頸,一臺伺服器的處理能力始終是會有上限的。
  3. 對於穩定性和可用性的要求,單機環境下無法提供,一旦單機應用掛了,整個系統就全部掛了,而分散式架構則不會存在這個問題,某一個模組不可用並不會導致整個系統的不可用。

SOA 架構

SOA 全稱為 Service Oriented Architecture,即面向服務架構。SOA 是一種架構理念。它的提出主要是解決服務之間的耦合問題。

SOA 對服務之間的解耦是一種比較粗粒度的劃分,比如我們的電商網站按服務可以拆分為:使用者模組,訂單模組,商品模組等。SOA 其本質上是服務的集合,然後服務間一般會通過 ESB 匯流排來進行通訊。比如之前比較常用的 webservice 就是一種 SOA 架構的實現。

微服務架構

微服務架構在 SOA 架構的基礎上做了進一步的細化,微服務架構和 SOA 架構並沒有本質上的區別,都是為了服務的解耦,只不過微服務架構更加關注服務的粒度,比如上面提到的使用者模組我們還可以進一步拆分成更細粒度的服務。

隨著微服務架構的普及,原本一個單體應用可能會被拆分成幾十個甚至更多的服務,從應用的壓力上來說,我們把壓力進行了分流,但是原本一個服務變成了多個服務對開發者和運維者來說也帶來了極大的挑戰,這也就隨之衍生了一些技術元件,比如服務與與服務之間如何通訊?單個服務如果是叢集如何實現負載均衡?配置如何進行統一管理?適合實現分流?如何實現監控等。

註冊中心

各個微服務相互之間需要進行呼叫,那麼服務與服務之間又是如何知道對方的呼叫資訊(如 ip,埠,路由等),最簡單最直接的辦法就是每個服務都維護一個其他需要呼叫的服務地址資訊,但是這樣會給開發和運維帶來相當大的工作量,當我們有某一個服務 A 的地址資訊發生變更,那麼只要呼叫了 A 服務的其他所有服務都要隨之修改。而且假如 A 服務當機了,其他服務也無法發現,當然,也可以做大發現,但是這會相當麻煩,而且每個服務都要重複實現這個功能,這會導致非常繁瑣和重複的工作,所以微服務常用元件中就有了註冊中心。

註冊中心是微服務架構中一個核心的基礎服務,主要用來管理所有的微服務,並且註冊中心需要實現服務上線和下線的感知。

也就是說我們所有的微服務都將自己的地址資訊註冊到註冊中心,然後其他呼叫者只需要維護註冊中心的地址即可,當一個服務下線的時候,註冊中心也會及時將該服務剔除。

常用的註冊中心有:EurekaconsulNacos,其他的還有 ZookeeperRedis 等也可以實現註冊中心。

遠端通訊協議

微服務之間各個服務可能會非常頻繁的呼叫,所以我們一定需要一款高效便捷的通訊協議來完成遠端通訊。

為什麼使用 rpc 而不直接使用 http

回答這個問題之前我們先來回答另一個問題,微服務之間能不能直接使用 http 來進行通訊?答案是肯定的,但是直接使用 http 來作為遠端通訊會有以下問題:

  • 請求和返回引數需要自己封裝,過程比較繁瑣。
  • http 協議是基於 tcp 協議實現的,每次連線和斷開需要三次握手和四次揮手,這過程會帶來一定的網路開銷。

基於上面兩個問題,我們需要另一種更加高效便捷的通訊方式來完成微服務之間的通訊,這就是 rpc 通訊。

RPC(Remote Procedure Call)遠端過程呼叫,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議,達到呼叫遠端服務就像呼叫本地方法一樣,也就是呼叫者並不知道這個方法會具體去呼叫哪個服務。

不過需要強調的是 RPC 並不是一種協議,這一點和 http 是有本質區別的,rpc 只是一種技術名詞,其底層實現也可以使用 http 協議,也可以基於 tcp 協議自己去進行改造。

RPC 主要是用來解決兩個問題:

  • 處理分散式架構中各個微服務之間的通訊問題。
  • 遠端呼叫時,呼叫者就像呼叫本地方法一樣方便。

常用的分散式服務之間遠端通訊元件有:feignopenfeigndubbo 等。

負載均衡

提到負載均衡大家的第一反應就是 nginx,一般我們使用 http 通訊時大部分都會使用 nginx 作為負載均衡來處理,那麼我們的微服務能否直接使用 nginx 來進行負載呢?

答案是可以的,但是我們為什麼不直接使用 nginx 作為服務轉發呢?我個人覺得主要有以下三個考慮:

  • nginx 主要是一款基於 http 來進行的 七層負載(當然其也能實現四層負載),而我們的微服務通訊之間不一定會基於 http 協議。
  • 如果使用了 nginx,等於是微服務之間又多引入了一個單點,我們還需要考慮 nginx 轉發的問題,還需要對其進行配置調優等。
  • 微服務使用了註冊中心來進行統一管理服務的上線和下線,而如果使用 nginx 那麼就需要使用 openresty 結合 lua 指令碼才能實現從註冊中心獲取服務。

也就是說直接使用 nginx 來進行負載的話,技術上是可行的,但是卻可能會引入一些新的問題,所以微服務之間的負載均衡並沒有直接選擇使用 nginx,而是重新開發了負載均衡元件。

常用的分散式服務之間負載均衡元件有:ribbon 等。

配置中心

假如我們某一個模組部署了幾十甚至上百個叢集部署,那麼如果每個服務都單獨使用自己的配置檔案的話,一旦修改某一個配置,那麼我們需要同時修改即使甚至上百個服務的配置,這是一個苦力活,所以我們就需要考慮讓這些服務共用同一套配置,這樣只要修改這一套配置,所有服務都能能生效。

配置中心主要就是用來解決這個問題,為了解決這個問題,配置中心需要具備以下能力:

  • 提供配置檔案的管理介面(dashboard),這樣使用者可以直接通過訪問 dashboard 來實現視覺化配置。
  • 配置中心配置修改之後,需要能及時通知到對應服務,讓對應服務修改最新配置。

常用的分散式服務之間負載均衡元件有:apollonacosSpring Cloud configdisconfdiamondZookeeper 等。

服務降級/熔斷

引入微服務我們的目的就是為了讓每一個微服務都成為一個獨立的單元,我們可以對每一個服務進行獨立擴充套件,實現高可用,假如現在有一個服務 A 因為一下子併發量過高導致請求堆積,那麼就會造成越來越多的請求阻塞,最終造成雪崩效應導致服務 A 當機,最終可能會導致整個微服務架構不可用,所以為了保證高可用用,微服務需要提供一種降級和熔斷措施。

降級也可以分為主動降級和被動降級,主動降級就是在高峰期比如我關閉一些非核心功能,如:評論,留言等功能。

而熔斷一般指的是某一個方法或者介面負載過高,或者說因為網路都動等原因造成響應超時或者失敗等,那麼這時候應該主動觸發熔斷,也就是對後續請求不再處理而是直接返回,當然這也要視具體業務來決定採用何種熔斷措施。

常用的分散式服務之間降級/熔斷元件有:HystrixSentinel 等。

服務閘道器

微服務架構是由單體服務架構發展而來,一般我們一個一個微服務架構其實是一個大的應用系統,那麼必然這一個大的系統有公共部分,比如:統一授權,統一路由,統一記錄日誌,也可以進行全域性的限流措施等。

不過微服務閘道器並不是必須的,這些工作也可以放到每個服務中進行處理,常用的微服務閘道器元件有:ZuulSpring Cloud GatWay

這麼多分散式元件該如何選擇

分散式架構中主要有六大元件,而每個元件又有不同的實現,看起來技術五花八門,感覺需要學的東西非常多,但是上面介紹了這麼多分散式元件,其實其主要就是三大型別:Spring Cloud NetflixSpring Cloud 官方Spring Cloud Alibaba,下面我們對這些分散式元件進行歸納分類,這樣大家在學習的時候就可以進行有目的的針對性學習:

Spring Cloud Netflix 是由 Netflix(美國奈飛)公司開源的一套分散式元件,這套元件應該也是大家比較熟悉的一套分散式元件,不過其只有 1.0 版本開源,2.0 之後就不再開源了,Spring Cloud 官方自己也提供了部分元件,而且基於 Feign 的基礎上改造成了 Open Feign

另外一套比較完整的分散式元件就是 Spring Cloud Alibaba,這是由阿里巴巴開源的的一套分散式元件,這套元件中的 dubbo 大家應該也是比較熟悉的,除了這兩套元件外,其他的也有一些可以用來作為分散式元件,比如 ZookeeperConsul 等,配置中心像 apollo 是攜程開源的,用的也比較多,所以大家學習的時候可以對同類元件進行了解,並對比其特性,然後選擇一套適合自己系統的元件使用。

除了上面的六大分散式元件外,分散式架構中還會涉及到另外兩個比較大的問題:

  • 分散式訊息

分散式訊息一般就使用訊息佇列,比如 Rabbit MQRocket MQ(阿里巴巴體系),kafka 等。

  • 分散式事務

分散式事務的話,Spring Cloud Alibaba 也提供了一個元件 seata 來實現。

另外分散式系統當中還涉及到鏈路監控相關問題,這方面可以選擇 sleuth + zipkinpinponitskywalking等等。所以說分散式架構解決了單體架構一些問題的同時,也帶來了一些問題,但是技術總是在向前發展的,比如現在號稱為了微服務而生的 Kubernetesk8s),又有號稱是下一代微服務架構的 Service Mesh等。

一門技術的誕生總是為了解決一些問題,所以還是那句話:業務才是推動技術發展的根本原因。只有隨著業務的發展出現了問題,才會去解決問題,才有更好的促進新技術的誕生。比如現在流行的 docker,也是為了解決微服務過多導致部署困難問題,任何一門技術能得到發展,它一定是解決了當前的痛點,否則我們為什麼要使用它?假如網際網路沒有興起,併發量始終很低,那麼微服務也不會興起,直接使用傳統的單點應用反而更簡單直接。

總結

本文主要講述了從單點應用到分散式架構的發展歷程,並且描述了微服務當中為什麼會誕生出一批元件,其根本原因就是為了解決微服務所帶來的的挑戰和問題,在文中最後對當前流行的分散式架構元件進行了分類整理,幫助大家梳理思路,這樣就可以做到有目的的進行鍼對性的學習,希望通過本文能讓大家對微服務相關元件有一個清晰的學習思路。

相關文章