從 MVC 到微服務,技術演變的必經之路 | 架構師實踐日
架構模式演進
CGI 模式
圖 1CGI 出現於 1993 年,圖 1 是 CGI 模式比較簡單的結構圖。
MVC 模式
開源電商軟體等都是採用 MVC 模式,MVC 模式是做軟體開發必學和必經歷的一個階段。
圖 21970 年提出了 MVC 的概念,當時的主機和客戶端早已凸顯了這個概念。如圖 2 所示,這是一個 MVC 模式的標準框架,做一個電商網站時,會把訂單、用戶、物流信息整合起來,做一個單體服務。
90 年代正是 Java 的時代,MVC 模式在這個時期開始變火。開發者把 MVC 的實踐引入 Java 世界。這個模式大放異彩後,各種語言都在借鑒。
SOA 模式
圖 3單體服務之後是 SOA 標準化(圖 3 ),在 SOA 裡面比較重要的是 Service bus。在企業架構里它起到很關鍵的作用,主要用來溝通各個服務組件,就像高速公路一樣,用來連接所有的村莊。
經過 MVC 模式後,單體模式開發速度很快,網民的速度增長也很快,伺服器接收的請求越來越多,慢慢需要做拆分才出現了 SOA 概念。雖然 1983 年提出了 SOA 的概念,但國內絕大部分企業至少是在 2000 年之後才開始 SOA 化。
Microservices 模式
圖 4圖 4 是 Microservices 的架構圖,每個 Host 裡面都有一個服務,它是獨立的。Microservices 於 2005 年出現,到現在仍備受關注和討論。一個概念由提出到落地,有很深層次的原因,具體後文分析。
何時使用微服務?
圖 5微服務並不能解決所有問題,所以一定要慎重考慮何時採用微服務。這張圖(圖 5 )很常見,單體服務的開發速度、代價都是比較低的,只有業務複雜到一定程度的時候才適合採用微服務。微服務其實是一個理念和框架,它對團隊的要求比較高,首先團隊所有成員都要認可這個事情,認可微服務的理念和治理框架,否則執行過程中會有很多問題。
圖 6
單體服務是把所有東西都部署在一塊,如圖 6 所示,這是最簡單的單體服務。在一定階段下,簡單代表高效,所有東西部署在一起時,整個過程的溝通效率會提高。
提到微服務時首先會提到康威定律,即當開發一個新的架構時,組織架構圖跟軟體架構圖是極其相似的。舉例說明,一個最簡單的 APP 開發架構包括移動端和服務端,如果把後台服務的人進行角色分層,會有運維、前端、做中間商業邏輯的後台開發者,就會發現軟體架構跟組織架構很相似。對微服務進行的拆分,其實也是對人員組織架構和角色職能的拆分。這兩者是很相似的。
為什麼 API 很重要?
微服務裡面最重要的是 API ,API 是服務公開化的方法,它是服務價值的精華體現。API 一定要可靠、可用、可讀,前兩點大部分人都能做到,但是可讀性,很多團隊卻做得很差。許多國內開發出來的開發系統文檔可讀性很差,看完之後可能還需要跟對方人員溝通。然而,無論是給內部開發,還是給外部用戶提供服務,都不希望產生更多的溝通。
出現這種情況的原因在於程序員本身的特性,程序員是寫程序的。很多程序員認為寫文檔不如寫程序有快感,這對於很多人來說是一種挑戰,也是一件很枯燥的事情。可是,對於公眾來說,文檔卻很重要,就像要成為一個好的架構師就要有良好的溝通能力,要做好微服務,API 可讀性很重要。
API 的設計永遠只有一次機會,特別是像七牛這類做對外服務的雲服務廠商。API 設計完之後,一直都會有人調用,由於無法知道誰正在調用 API ,就無法讓對方停用,從而無法升級。所以 API 只有一次機會,發布後就定了型。例如京東的部門不算多,但是 API 發布之後有很多部門在調用。團隊做升級時,會通知所有部門停用 API 並且要升級的事情。但在升級完成之後,還是會有部門詢問 API 為什麼不通了?所以要符合可靠可用這兩點,只有一次機會。
API Gateway
通常在企業里用的是 API Gateway ,因為它們可以直連。用這個方式有幾個優點:
- 一些通用的事情可以在 Gateway 上面做,包括所有API的請求調用量、請求響應時間、4xx 和 5xx 等錯誤出錯數,這些東西可以通過日誌或者其它方式很實時展現出來。
- 所有的服務其實都需要負載均衡,可以在 API Gateway 統一做一些規則、限流。
API 的設計要素
- 版本號
永遠要清楚誰調了哪一個版本的 API 。雖然 API 發布只有一次機會,但有版本號之後能有所防備,做版本升級或版本兼容性的時候會更容易。
- RequstID
RequstID 可以有效地提高效率,並在後台加快 Bug 複查和定位問題。給客戶端的請求都有ID ,客戶端發現有問題或者返回失敗時,直接把 RequstID 給伺服器端,伺服器端拿 RequstID 到後台日誌里查,就可以定義到問題。實際上從客戶端發過來的一個請求,在後台經過了十幾個微服務。而這十幾個微服務調用的上下文串聯和跟蹤的工作,都需要一個 RequstID ,能夠從請求端開始,往後去傳遞,每個人都要記錄 RequstID ,出現問題時,把微服務的日誌拉出來,就可以很快定位問題的來源。
- 安全認證和 Signature
要知道誰調了什麼東西,並且保證被調的東西不被篡改。外部調動 API 時都會做安全認證,但是內部調用時不一定會做安全認證。但往往很多時候事情的發展都是出乎意料的,有可能因為老闆的決定或者業務的變化,API 需要對外開放,如果前期做了安全認證,就可以避免很多麻煩。
- 文檔可讀性
一定要寫好文檔,不論是對客戶、領導還是外部用戶,文檔寫得漂亮更能彰顯性能的優點。
- Error Code 和 Message 的問題
以前有兩個流派的 Error Code 和 Message 的展現方式。第一種是調用一個請求,對方把錯誤碼寫在 HTTP 的 Code 裡面,可能用 400、404、409。第二種調法是客戶端給服務端發了一個請求,返回 200,在 Body 裡面有一個真正的 Code ,通過觀察這個 Code 來判斷調用是否成功,不成功就會通知 Error Message 。經過幾年的實踐後推薦使用第二種,因為在微服務相互調用時,它的協議不僅僅局限於 HTTP ,可能是其它方式的調用,直接用這個協議表示這種方式的調用狀態並不合適,相反在裡面表示一個狀態會更好。
圖 7圖 8做一個案例分析,如圖 7 所示是一家公司的架構圖,野蠻生長的時候更注意的是功能的效率。架構很簡單,沒有服務需要進行小拆分,都是放在一起形成一個大塊整體。前期功能性開發很快,但到了一定階段後,不管是調用效率還是開發效率都會顯現出不足之處。所以進行一個拆分,把大塊的拆成十幾個、二十幾個(如圖 8 所示)。基本上每家在做架構升級時,都會把一堆大的東西拆成小的,即微服務;而把四個變六個,就是微服務的架構升級。
然而在升級的過程中,也會存在一些「戰爭」,如下:
- 效率與規則的戰爭。
- 習慣與規範的戰爭。
- 迭代與優化的戰爭。
- 產品與開發的戰爭。
- 業務爆發與服務能力的戰爭。
業務爆發有兩種:
一種是用戶量、請求量上來了,服務卻跟不上;
其次是是業務模式增加,需要開發更多的功能,但是開發更多功能時,卻會導致服務能力和服務質量的下降。
微服務變火的原因
十年前的技術到近幾年才開始變得很火的原因,需要從微服務的優劣勢進行分析,主要是以下幾個原因:
第一,微服務有按需伸縮的特點。按需伸縮是微服務的優點也是它的缺點。它的缺點是會帶來部署與監控運維的成本,每個部署都要監控要運維,每個微服務後面都包含一個缺點。
第二,每個微服務都可以獨立部署,但它的缺點是增加機器數量與部署成本。
第三,業務獨立。微服務的業務很獨立,但有服務依賴、治理、版本管理、事務處理的缺點。
第四,技術多樣性。它帶來的成本是環境部署成本,因為每個語言環境不同。以及約定成本,和跨語言如何調,都是成本。
微服務如何治理?
當了解微服務的優缺點之後,自然會考慮到如何治理微服務,可以通過以下幾點進行治理:
第一,運行狀態治理。監控、限流、SLA 、LB 、日誌分析,都有一些成熟的開發組件,現在有開源版本的也有雲端服務,比如做性能監控的 APM SaaS 等。限流不需要做多麼複雜的東西,SLA 自己有日誌分析就可以。
第二,服務註冊與發現,現在已有一些比較完善的解決方案。
第三,部署。部署和布置上的成本大部分被解決了,不管是用容器還是虛擬機,都可以快速部署、快速複製鏡像,再進行擴容。當本機上的線上環境所有服務都布好了,在本地開發是最方便的,且不依賴於其他同事提供服務軟體。
第四,調用。注重安全、容錯、服務降級、調用延遲。
服務註冊
服務註冊有兩種方案:第一種是客戶端發現,第二種是服務端發現。這兩種方案是很主流的方案,都有各自的優缺點。下面就是對這兩種方案的介紹。
客戶端發現
首先分析客戶端發現。所有的路由表信息存在客戶端,客戶端知道應該調用哪台機器設備的哪個微服務後,再去做相應的 Load Balance ,比如微服務有四個服務,每個服務的權重都推送到客戶端里,客戶端再決定調用哪個服務。客戶端擁有所有微服務的伺服器列表和埠,每個客戶端和 Service Registry 都會保持一個長連接,常見做法是每個客戶端和 Service Registry 建立一個連接,當微服務後台增加或減少一個微服務,都可以實時地通知客戶端。但是這個方案也存在優缺點。下面就是對其優缺點的分析。
優點:快,可以直接跟微服務直連。
缺點:
- Service Registry 很多時候會成為一個瓶頸,當客戶端越多瓶頸越明顯,任何一個微服務有更新,都必須通知所有客戶端,對實時性的要求比較高。
- 升級痛苦。因為微服務嵌在客戶端,很難知道哪些內部用戶是用哪個版本的 API ,同時也很難約定大家在同一個時間升級。
- 維護成本高。
服務端發現
服務端發現,通常的服務範圍是,所有對外提供服務的介面,七牛用的也是這個方式,因為有成千上萬個客戶端,不可能把路由表寫在七牛的客戶端上面。當然這種方式也存在優缺點。
優點是:所有的服務可控。增加或刪除一個服務時,不需要通知客戶端,所有客戶端可以脫離管控範圍。
缺點:
- 治理效率和瓶頸是一個很難的問題。
- 標準的 HTTP 協議問題不大,如果是自定義協議會很麻煩,需要做的工作會很多。
微服務的部署
平台
微服務的部署和治理跟雲平台沒有必然的聯繫,用微服務並不意味著只能用容器,仍然可以用 IaaS 、 PaaS 、老的數據中心等平台,這個跟基礎設施並沒有多大的關係,但使用容器可以更好的解決某些方面的問題。
手段
部署方式一般分為手動部署和使用腳本來自動化部署。大公司有基礎設施自動化、應用部署自動化平台,一點按鈕就可以完成測試、打包、上線、監控、部署,但是小公司基本上沒有這種基礎設施建設。目前還沒有公司把基礎設施建設進行開源,因為每個公司的基礎設施自動化測試不同,業務場景不同,所以不一定適用。
應用的自動化部署。 2010 年 PaaS 很火,很多人想把這個部署做成自動化。因為當部署應用特別方便的時,大家才願意做拆分。管理一台機器,部署一件事情、一個服務,跟部署十個是有明顯的時間成本的,如果時間成本降低,大家更願意進行拆分。
還有一種方式是 Image 的部署。把虛擬機打包成一個鏡像(它的擴展很迅速),常見做法是鏡像裡面不放代碼而是放腳本。每次部署一個新的用戶服務時,需要到上面取一個最新的包來部署新的工作,而不是把代碼放上去。因為鏡像的製作成本比較高,更新麻煩。
容器和微服務
近幾年微服務突然變火,其實跟容器有很大的關係。
第一容器夠小,可以解決微服務獨立部署的問題,解決微服務對機器數量的訴求。
第二容器獨立,可以解決多語言的問題。
第三是開發環境與生產環境相同,針對非 Windows 開發者,如果使用容器技術,就比較容易使本機的開發環境與生產環境相同。Windows 開發者需要自己部署一台伺服器,保持伺服器跟線上相同。當一個公司裡面有多語言時,有一個鏡像就容易解決這個問題。
第四是容器效率高,省錢。
第五是代碼和 image 一體化。簡網以前以服務為核心,每個服務的管理體系比較難做,現在用 Docker 後可以把 image 做完,再把服務和 image 做一一對應後,管理就可以實現一體化。
第六是容器的橫向擴展。容器很容易橫向擴展,但縱向擴展對公司來說更有意義。從整個公司的層面來看,機器利用率並不好,這是一個客觀存在的問題,當時的技術無法解決問題,因為要多少機器是業務決定的,運維並沒有決策權。此時,可以把內存和 CPU 做一個動態調整,最初,可以調低一點,當增上來之後再進行擴容。
但是這樣做仍然存在一些問題。
第一是 Image 管理問題,有很多服務做,但是誰真正有資格做 Image 管理呢?Image 鏡像越來越多,如果開放給所有人,每發布一個升級就多一個鏡像,那這是需要治理的。
第二是系統安全管理問題,在 KVM 裡面套一個 Docker ,用 KVM 做隔離,這樣做,不僅加強了安全性,又利用了 Docker 快速部署、快速開發微服務管理的體系,系統安全上,短期內無解,但在公司內部運用還可以。
第三是授權管理問題,授權管理很多時候是特指資料庫,如果服務是有狀態的,擴容的時候需要做資料庫授權,這個授權的操作和管理是有難度的,特別是發布後發現系統有漏洞,管理的難度很高。
第四是系統成熟度問題,雖然現在 Docker 很火,但整體來說系統成熟度沒有達到理想的狀態,運用過程中多少會出現一些問題。
第五是社區成熟度問題,Docker 在兩三年的時間內突然變熱,但社區成熟度是需要時間沉澱的。
推薦閱讀: