從單體架構到微服務架構的演進歷程
一、單體架構
1.1 什麼時候用單體架構
在創業初期或專案開始時,專案整體功能比較少,開發人員也少,且專案需要用最少時間開發出來,用 MVP 方式快速進行市場驗證是否可行,這時候就可以用單體架構進行快速開發。
1.2 單體架構設計舉例-電商應用
功能分析:
拿淘寶網來舉例,現代電商網站功能是很複雜的,有多少功能呢?可以看看我前面的文章《電商產品設計全攻略》讀書筆記(https://www.cnblogs.com/jiujuan/p/14452748.html#1574269892) 裡的電商管理系統和電商平臺產品結構 2 小節。
拿淘寶網舉例的話,當然是最早期的淘寶網 - -!,它最簡單的 3 個功能:商品展示,使用者下單,訂單中心。這三個功能構成一個最簡單的電商業務流程。
展示給使用者看的商品頁面以及使用者購買商品的操作功能。那我們需要對使用者和訂單進行管理,怎麼辦?
就需要一個管理後臺來對使用者訂單進行管理。如此簡單分析過後,就知道了電商網站應用功能。
應用功能架構圖:
程式架構設計:
這時候我們開發的單體應用程式,部署在應用伺服器上。程式架構可能採用 MVC 這種程式架構模式。
當然也有可能什麼架構都不用,直接擼程式碼了,所有的程式都混合在一起,這就是所謂的“大泥球”單體,這是一種糟糕的開發方式。
java 裡最常用的 MVC 框架,比如 SpringMVC 框架。
- 劃分模組
比如根據上面電商功能架構圖,在程式裡可以把電商功能劃分為相對應的模組,如使用者模組,訂單模組,商品模組。
這時程式裡不管是前臺功能,後臺功能都有這些模組。
(應用程式模組)
這時候應用程式模組都在一個大的單體專案裡,前臺功能和管理後臺共用一套程式碼。
- 模組裡的功能
比如前臺商品模組,就有商品列表,商品詳情頁等頁面功能。
- 程式開發
編寫程式時可能應用 MVC 這種程式設計模式來進行程式程式碼開發。
程式部署架構圖:
編寫的程式程式碼部署到應用伺服器上,使用者的所有資料儲存到 MySQL 資料庫裡。
程式和 MySQL 都部署在同一臺伺服器上。
二、單體架構演進
2.1 MySQL 效能瓶頸-快取
隨著專案上線,公司對專案加大力度推廣和運營,使用者數越來越多。
有一天,使用者投訴說,商品詳情頁面瀏覽好慢。如是你一番操作猛如虎,發現詳情頁顯示慢,效能瓶頸出現在資料庫 MySQL 上,
MySQL 在使用者訪問高峰時,扛不住那麼大的訪問量。這時你想到的解決方法,可以用快取來快取一部分資料,不必每次都到 MySQL 取資料,
可以用 Redis 來快取部分商品資訊資料。如是,增加一個 Redis 快取,架構圖如下:
2.2 MySQL 讀寫分離
這時候,你也可能想到另外的一種方法:資料的讀寫分離,減輕對 MySQL 訪問壓力。
經過上面 2 種措施改進後,商品詳情頁訪問速度開始變快,訪問正常了。
但是這種舒服日子沒過幾個月,又有使用者開始反饋頁面訪問比較慢。
你又一番埋頭辛苦分析,發現是單臺伺服器負載高,單臺伺服器的效能已經到了極限,它已經承載不了那麼多使用者的訪問。
如是你想,把資料儲存和應用程式部署到 2 臺伺服器上,減輕伺服器的負載壓力。
2.3:資料儲存和應用程式伺服器分離
於是你申請買了一臺伺服器,把 MySQL 和 Redis 都部署在這臺新買的伺服器上,讓原來那臺伺服器負載得到緩解。
新的架構部署成功後,使用者訪問頁面又恢復正常。
但是隨著業務發展越來越好,新增使用者越來越多,應用伺服器的負載又居高不下了。
這時候要增加新的應用伺服器了,這樣做是最簡單的。多臺應用伺服器形成叢集,那怎麼訪問這些應用伺服器?才能使每臺伺服器負載保持平衡,或者效能好的多接受一些使用者訪問?這時候就要用到負載均衡了。
2.4 叢集-分散式
叢集-負載均衡(多臺應用伺服器)
部署多臺應用伺服器形成一個應用伺服器叢集,前面使用者透過負載均衡器來進行服務的訪問。
比較常用的負載均衡軟體有 Nginx、LVS、KeepAlived 等等。
還有硬體負載均衡,比如 F5 等。
部署後,頁面訪問又恢復了正常。
過了幾個月,資料伺服器也出現負載過高情況,這時候可以把 Redis 快取和 MySQL 分離,部署到不同伺服器上。
隨著資料量增加,把 Redis 部署為分散式快取。
資料庫分離和 Redis 分散式快取
把 MySQL 和快取 Redis 部署到不同的伺服器上。
隨著快取資料的增多,Redis 也部署為主從模式,然後到 Redis Cluster 叢集模式,也就是 Redis 的分散式快取。
此時資料儲存伺服器負載得到緩解,訪問恢復正常。
由於業務發展太快,使用者變得更多,資料庫又出現了效能瓶頸,這時可以對資料庫進行分庫分表
分庫分表
為了進一步的降低資料庫由於資料量太多,訪問太大而造成的瓶頸,可以對資料庫進行分庫分表,減輕資料庫的訪問壓力。
三、應用程式發展演進
3.1 應用程式功能變化-硬體發展
上面畫的架構圖都是後端技術部分,服務端架構從單體到叢集再到分散式的演進。
那麼前面給使用者使用的應用程式呢?也是在變化之中。
比如在《淘寶技術這十年》裡的淘寶網的發展變化,剛開始時是一個很簡單的 PC 端頁面,到後來隨著手機普及,移動網際網路發展起來,
手機應用就出現了。淘寶 APP 也隨之出現。隨著國民應用微信逐漸發展壯大,小程式也成為第三種網際網路程式應用形式。當然,還有其它終端,比如平板 ipad,自動售貨機等等各種終端。
上面是不同硬體出現,程式應用承載出現不同形式。
(多終端使用者出現後的架構圖)
那麼淘寶網的功能呢?當然增加了很多。
還孵化出了多種不同的業務應用,比如天貓,1688,支付寶,聚划算,淘寶旺旺等等很多應用。
在今年 2013.1 再去開啟淘寶 APP 看看,裡面的功能多到眼花繚亂。
現在的電商系統有哪些子系統,系統裡都有啥功能,可以看看我之前釋出的這篇文章。
3.2 多端程式單體架構
多種終端的出現,當然不是一下子就出來的,都有一個發展過程,只不過到寫這篇文章為止,出現了上面說的PC,手機 APP、小程式,平板等多個終端,最常用的還是前面 3 種。
最開始開發程式時,應用程式要適應多個終端,最簡單的方式就是複製 PC 端的程式碼到多個終端程式裡。按照上面 1.2 小節的電商最簡單業務功能模組實現,多終端應用程式功能架構如下:
(後端功能模組架構圖)
上面的三種終端程式應用,還是使用同一個 MySQL 資料庫,也及是說訂單資料、使用者資料等都儲存在一個庫中。
可能會問,怎麼區分訂單來自哪一個終端?
可以給訂單資料一個型別標識來進行區分,訂單是從哪一個終端過來的。
那管理後臺呢?
多終端程式可以共用一個管理後臺。
多終端的後臺功能模組都搞起來了,但是這種程式模組架構有什麼弊端缺點呢?
- 程式碼重複
- 增加/修改功能複雜:比如說要修改一個訂單模組的功能,需要修改 3 個終端的後臺程式碼
- 程式碼維護複雜:每次維護程式碼都需要動 3 個後端的程式碼
那有沒有辦法可以改進上面所說的情況?
能不能把 3 個後端重複模組程式碼合併為一個,統一向前端提供服務,當然是可以的。怎麼做?
- 前後端分離 - 把前後端程式碼進行分離,前端展示操作頁面和後端功能模組分離
- 抽象公共模組 - 把多個後端公共模組進行抽象為一個模組,為前端提供統一服務
3.3 前後端分離,抽象公共模組
頁面前後端分離,其實在上面 1.1 小節的單體架構中也可以這樣實施前後端分離。
多個終端當然也可以進行前後端分離,這樣不用開發多個終端的頁面,程式程式碼進行適配就可以了。前端現在有很多種多終端適配的技術。
後端的多個相同功能模組進行抽象,變成一個共用模組,對外提供服務。
這種方式提供功能服務我想到的有 3 種方式:
第一種:單體結構-函式提供介面
後端還是在一個單體工程下面,但是公共功能抽象為一個函式或物件介面,對外提供服務。
後端其他模組引入這個模組然後呼叫函式或者物件,完成程式功能開發。
應用程式結構圖如下:
第二種:用 Maven 當作一個遠端包引入
在 java 裡,用 maven 可以引入一個遠端包進行使用。我們可以用這種方式引入公共功能包。
在 Go 裡,用 module 模組方法引入遠端包使用。
第三種:RPC 方式呼叫
這種方式是把應用程式裡的公共模組功能變成一個獨立的服務,對外提供服務。這個”外“是公司內部的業務可以呼叫這個服務。公司以外的應用就不可以呼叫這個服務。
這裡也有2種方式,
第一種:只是公共模組獨立提供服務,資料庫還是共用。
第二種:資料庫隨著公共模組一起,獨立對外提供服務。
我們來討論第二種情況,既然要作為一個獨立的服務存在,它就是自適應自維護的,此時資料庫變成獨立資料庫,跟著它的服務模組一起獨立。
此時不僅應用模組進行分離,應用伺服器也進行了分離。
這時候就有點微服務的味道了。
四、微服務架構演進
對於微服務的瞭解,可以看看我前面關於微服務系列文章的講解,比如下面文章:
微服務的技術架構實際是一個體系,它是由很多技術組成的。
4.1 以 SpringCloud 為基礎的微服務技術體系
最開始最 netflix 公司開源的以 springcloud 為基礎的微服務技術體系,它把微服務體系開源了。不過後來 netflix 放棄維護它開源的微服務框架。
但是 spring 框架的公司和阿里巴巴都開源了自己的以 springcloud 為基礎的微服務體系,阿里巴巴叫 springcloud-alibaba。
阿里還開源了另外一個微服務框架 dubbo。
這些框架都提供了一些主要功能:服務發現和註冊,限流熔斷,鏈路追蹤,配置中心,閘道器等功能。
SpringCloud 微服務體系有哪些缺點?
- 程式碼侵入性強 - 業務層中需要加入治理層程式碼,與治理層混淆在一起
- 元件多 - 元件多,學習成本就變高
- 治理功能不全 - 比如協議轉換、動態請求路由、灰度釋出等功能
- 無法實現語義無關性 - 只能是一種語言或幾種語言實現,無法做到與程式語言無關
針對以上的一些問題,就出現了 Service Mesh 這種架構,它作為一個基礎設施層,真正做到與業務解耦,與語言無關,解決複雜網路下微服務與微服務之間通訊問題。
其實就是把通訊相關功能分離出來,與業務系統徹底解耦。
4.2 Service Mesh
Service Mesh 解決複雜網路下微服務與微服務之間通訊問題,它的實現形態一般為輕量級的網路代理,與應用以邊車(SideCar)模式部署。
第一代ServiceMesh
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
來看一個全域性圖:
(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。
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
springcloud 與 ServiceMesh 的區別:
(from:https://medium.com/codex/a-spring-cloud-compatible-service-mesh-6ce58c571012)
五、參考
-
https://philcalcado.com/2017/08/03/pattern_service_mesh.html Pattern: Service Mesh,詳細介紹了微服務到 Servie Mesh的演進
-
https://medium.com/codex/a-spring-cloud-compatible-service-mesh-6ce58c571012
-
https://www.cnblogs.com/jiujuan/p/14452748.html 《電商產品設計全攻略》讀書筆記
-
https://www.cnblogs.com/jiujuan/p/13301055.html 微服務架構學習與思考(04):微服務技術體系