服務架構學習與思考(12):從單體架構到微服務架構的演進歷程

九卷 發表於 2023-01-25
微服務

從單體架構到微服務架構的演進歷程

一、單體架構

1.1 什麼時候用單體架構

在創業初期或專案開始時,專案整體功能比較少,開發人員也少,且專案需要用最少時間開發出來,用 MVP 方式快速進行市場驗證是否可行,這時候就可以用單體架構進行快速開發。

1.2 單體架構設計舉例-電商應用

功能分析:

拿淘寶網來舉例,現代電商網站功能是很複雜的,有多少功能呢?可以看看我前面的文章《電商產品設計全攻略》讀書筆記(https://www.cnblogs.com/jiujuan/p/14452748.html#1574269892) 裡的電商管理系統電商平臺產品結構 2 小節。

拿淘寶網舉例的話,當然是最早期的淘寶網 - -!,它最簡單的 3 個功能:商品展示,使用者下單,訂單中心。這三個功能構成一個最簡單的電商業務流程。

展示給使用者看的商品頁面以及使用者購買商品的操作功能。那我們需要對使用者和訂單進行管理,怎麼辦?

就需要一個管理後臺來對使用者訂單進行管理。如此簡單分析過後,就知道了電商網站應用功能。

應用功能架構圖:

image-20230124022153045

程式架構設計:

這時候我們開發的單體應用程式,部署在應用伺服器上。程式架構可能採用 MVC 這種程式架構模式。

當然也有可能什麼架構都不用,直接擼程式碼了,所有的程式都混合在一起,這就是所謂的“大泥球”單體,這是一種糟糕的開發方式。

java 裡最常用的 MVC 框架,比如 SpringMVC 框架。

  1. 劃分模組

比如根據上面電商功能架構圖,在程式裡可以把電商功能劃分為相對應的模組,如使用者模組,訂單模組,商品模組。

這時程式裡不管是前臺功能,後臺功能都有這些模組。

image-20230124110006299

​ (應用程式模組)

這時候應用程式模組都在一個大的單體專案裡,前臺功能和管理後臺共用一套程式碼。

  1. 模組裡的功能

比如前臺商品模組,就有商品列表,商品詳情頁等頁面功能。

  1. 程式開發

編寫程式時可能應用 MVC 這種程式設計模式來進行程式程式碼開發。

程式部署架構圖:

image-20230124122806802

編寫的程式程式碼部署到應用伺服器上,使用者的所有資料儲存到 MySQL 資料庫裡。

程式和 MySQL 都部署在同一臺伺服器上。

二、單體架構演進

2.1 MySQL 效能瓶頸-快取

隨著專案上線,公司對專案加大力度推廣和運營,使用者數越來越多。

有一天,使用者投訴說,商品詳情頁面瀏覽好慢。如是你一番操作猛如虎,發現詳情頁顯示慢,效能瓶頸出現在資料庫 MySQL 上,

MySQL 在使用者訪問高峰時,扛不住那麼大的訪問量。這時你想到的解決方法,可以用快取來快取一部分資料,不必每次都到 MySQL 取資料,

可以用 Redis 來快取部分商品資訊資料。如是,增加一個 Redis 快取,架構圖如下:

image-20230124122020910

2.2 MySQL 讀寫分離

這時候,你也可能想到另外的一種方法:資料的讀寫分離,減輕對 MySQL 訪問壓力。

image-20230124122113934

經過上面 2 種措施改進後,商品詳情頁訪問速度開始變快,訪問正常了。

但是這種舒服日子沒過幾個月,又有使用者開始反饋頁面訪問比較慢。

你又一番埋頭辛苦分析,發現是單臺伺服器負載高,單臺伺服器的效能已經到了極限,它已經承載不了那麼多使用者的訪問。

如是你想,把資料儲存和應用程式部署到 2 臺伺服器上,減輕伺服器的負載壓力。

2.3:資料儲存和應用程式伺服器分離

於是你申請買了一臺伺服器,把 MySQL 和 Redis 都部署在這臺新買的伺服器上,讓原來那臺伺服器負載得到緩解。

image-20230124122240609

新的架構部署成功後,使用者訪問頁面又恢復正常。

但是隨著業務發展越來越好,新增使用者越來越多,應用伺服器的負載又居高不下了。

這時候要增加新的應用伺服器了,這樣做是最簡單的。多臺應用伺服器形成叢集,那怎麼訪問這些應用伺服器?才能使每臺伺服器負載保持平衡,或者效能好的多接受一些使用者訪問?這時候就要用到負載均衡了。

2.4 叢集-分散式

叢集-負載均衡(多臺應用伺服器)

部署多臺應用伺服器形成一個應用伺服器叢集,前面使用者透過負載均衡器來進行服務的訪問。

image-20230124124859989

比較常用的負載均衡軟體有 Nginx、LVS、KeepAlived 等等。

還有硬體負載均衡,比如 F5 等。

部署後,頁面訪問又恢復了正常。

過了幾個月,資料伺服器也出現負載過高情況,這時候可以把 Redis 快取和 MySQL 分離,部署到不同伺服器上。

隨著資料量增加,把 Redis 部署為分散式快取。

資料庫分離和 Redis 分散式快取

image-20230124130955865

把 MySQL 和快取 Redis 部署到不同的伺服器上。

隨著快取資料的增多,Redis 也部署為主從模式,然後到 Redis Cluster 叢集模式,也就是 Redis 的分散式快取。

此時資料儲存伺服器負載得到緩解,訪問恢復正常。

由於業務發展太快,使用者變得更多,資料庫又出現了效能瓶頸,這時可以對資料庫進行分庫分表

分庫分表

為了進一步的降低資料庫由於資料量太多,訪問太大而造成的瓶頸,可以對資料庫進行分庫分表,減輕資料庫的訪問壓力。

三、應用程式發展演進

3.1 應用程式功能變化-硬體發展

上面畫的架構圖都是後端技術部分,服務端架構從單體到叢集再到分散式的演進。

那麼前面給使用者使用的應用程式呢?也是在變化之中。

比如在《淘寶技術這十年》裡的淘寶網的發展變化,剛開始時是一個很簡單的 PC 端頁面,到後來隨著手機普及,移動網際網路發展起來,

手機應用就出現了。淘寶 APP 也隨之出現。隨著國民應用微信逐漸發展壯大,小程式也成為第三種網際網路程式應用形式。當然,還有其它終端,比如平板 ipad,自動售貨機等等各種終端。

上面是不同硬體出現,程式應用承載出現不同形式。

image-20230124185223955

​ (多終端使用者出現後的架構圖)

那麼淘寶網的功能呢?當然增加了很多。

還孵化出了多種不同的業務應用,比如天貓,1688,支付寶,聚划算,淘寶旺旺等等很多應用。

在今年 2013.1 再去開啟淘寶 APP 看看,裡面的功能多到眼花繚亂。

現在的電商系統有哪些子系統,系統裡都有啥功能,可以看看我之前釋出的這篇文章

3.2 多端程式單體架構

多種終端的出現,當然不是一下子就出來的,都有一個發展過程,只不過到寫這篇文章為止,出現了上面說的PC,手機 APP、小程式,平板等多個終端,最常用的還是前面 3 種。

最開始開發程式時,應用程式要適應多個終端,最簡單的方式就是複製 PC 端的程式碼到多個終端程式裡。按照上面 1.2 小節的電商最簡單業務功能模組實現,多終端應用程式功能架構如下:

image-20230124193811484

​ (後端功能模組架構圖)

上面的三種終端程式應用,還是使用同一個 MySQL 資料庫,也及是說訂單資料、使用者資料等都儲存在一個庫中。

可能會問,怎麼區分訂單來自哪一個終端?

可以給訂單資料一個型別標識來進行區分,訂單是從哪一個終端過來的。

那管理後臺呢?

多終端程式可以共用一個管理後臺。

多終端的後臺功能模組都搞起來了,但是這種程式模組架構有什麼弊端缺點呢?

  1. 程式碼重複
  2. 增加/修改功能複雜:比如說要修改一個訂單模組的功能,需要修改 3 個終端的後臺程式碼
  3. 程式碼維護複雜:每次維護程式碼都需要動 3 個後端的程式碼

那有沒有辦法可以改進上面所說的情況?

能不能把 3 個後端重複模組程式碼合併為一個,統一向前端提供服務,當然是可以的。怎麼做?

  1. 前後端分離 - 把前後端程式碼進行分離,前端展示操作頁面和後端功能模組分離
  2. 抽象公共模組 - 把多個後端公共模組進行抽象為一個模組,為前端提供統一服務

3.3 前後端分離,抽象公共模組

頁面前後端分離,其實在上面 1.1 小節的單體架構中也可以這樣實施前後端分離。

多個終端當然也可以進行前後端分離,這樣不用開發多個終端的頁面,程式程式碼進行適配就可以了。前端現在有很多種多終端適配的技術。

後端的多個相同功能模組進行抽象,變成一個共用模組,對外提供服務。

這種方式提供功能服務我想到的有 3 種方式:

第一種:單體結構-函式提供介面

後端還是在一個單體工程下面,但是公共功能抽象為一個函式或物件介面,對外提供服務。

後端其他模組引入這個模組然後呼叫函式或者物件,完成程式功能開發。

應用程式結構圖如下:

image-20230124225610407

第二種:用 Maven 當作一個遠端包引入

在 java 裡,用 maven 可以引入一個遠端包進行使用。我們可以用這種方式引入公共功能包。

在 Go 裡,用 module 模組方法引入遠端包使用。

第三種:RPC 方式呼叫

這種方式是把應用程式裡的公共模組功能變成一個獨立的服務,對外提供服務。這個”外“是公司內部的業務可以呼叫這個服務。公司以外的應用就不可以呼叫這個服務。

這裡也有2種方式,

第一種:只是公共模組獨立提供服務,資料庫還是共用。

第二種:資料庫隨著公共模組一起,獨立對外提供服務。

我們來討論第二種情況,既然要作為一個獨立的服務存在,它就是自適應自維護的,此時資料庫變成獨立資料庫,跟著它的服務模組一起獨立。

此時不僅應用模組進行分離,應用伺服器也進行了分離。

這時候就有點微服務的味道了。

image-20230124233108779

四、微服務架構演進

對於微服務的瞭解,可以看看我前面關於微服務系列文章的講解,比如下面文章:

微服務的技術架構實際是一個體系,它是由很多技術組成的。

4.1 以 SpringCloud 為基礎的微服務技術體系

最開始最 netflix 公司開源的以 springcloud 為基礎的微服務技術體系,它把微服務體系開源了。不過後來 netflix 放棄維護它開源的微服務框架。

但是 spring 框架的公司和阿里巴巴都開源了自己的以 springcloud 為基礎的微服務體系,阿里巴巴叫 springcloud-alibaba。

阿里還開源了另外一個微服務框架 dubbo。

這些框架都提供了一些主要功能:服務發現和註冊,限流熔斷,鏈路追蹤,配置中心,閘道器等功能。

SpringCloud 微服務體系有哪些缺點?

  1. 程式碼侵入性強 - 業務層中需要加入治理層程式碼,與治理層混淆在一起
  2. 元件多 - 元件多,學習成本就變高
  3. 治理功能不全 - 比如協議轉換、動態請求路由、灰度釋出等功能
  4. 無法實現語義無關性 - 只能是一種語言或幾種語言實現,無法做到與程式語言無關

針對以上的一些問題,就出現了 Service Mesh 這種架構,它作為一個基礎設施層,真正做到與業務解耦,與語言無關,解決複雜網路下微服務與微服務之間通訊問題。

其實就是把通訊相關功能分離出來,與業務系統徹底解耦。

4.2 Service Mesh

Service Mesh 解決複雜網路下微服務與微服務之間通訊問題,它的實現形態一般為輕量級的網路代理,與應用以邊車(SideCar)模式部署。

第一代ServiceMesh

image-20230125001047147

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

來看一個全域性圖:

image-20230125001700722

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

綠色:應用服務

藍色:SideCar

第二代 ServiceMesh:istio

第一代 Service Mesh 是由獨立執行的單機服務代理構成,為了提供統一的控制入口,演進出了統一的控制皮膚,稱為 control plane。

控制皮膚(control plane)和資料皮膚(data plane,即邊車代理)進行互動,比如策略下發、資料採集等。這就是以Istio為代表的第二代Service Mesh。

image-20230125002338290

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

image-20230125002415442

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

springcloud 與 ServiceMesh 的區別:

image-20230125002930584

​ (from:https://medium.com/codex/a-spring-cloud-compatible-service-mesh-6ce58c571012)

五、參考