為什麼要學習架構?
因為架構思路是編碼的基石,如果腦海中沒有架構思路,編碼就是一葉障目,看不到全局也就不可能寫出好的代碼。先别急着撸代碼,先看看理論,有個大緻的概念。
原理可能看看就能懂,但是隻有經曆了實踐,體會了痛點,才能了解和吸收。
當下網際網路行業的火爆,使用者量的激增,讓各大公司開始考慮擴容和重構,微服務是首先引入眼簾的。然而據我所知,國内不少中小規模的技術團隊對微服務的概念都不甚了解,對該不該引入微服務也不置可否。還有一些技術團隊,沒有考慮實際業務場景,隻是為了追求技術熱點,盲目引入微服務,但又缺乏相應的技術掌控能力,最後影響了業務的穩定性。
一、架構選型
組織溝通方式決定系統設計。
一定要根據自己的公司體量、業務形态來選擇架構,好的架構都是一步步演進過來的。不要過于崇尚新技術、新架構。
架構的演進一定是源于痛點。
如果你不知道目前架構有什麼問題,有什麼痛點,那麼一定無法演進。有很多公司或個人看到微服務很火爆,就開始盲目追求。這樣隻能是緣木求魚。
對于微服務,我隻能說,單挑死無全屍。
微服務的首要問題:
- 該不該引入微服務
- 微服務體系需要哪些技術
- 團隊怎麼演進,怎麼落地
架構選型
根據康威定律:康威定律
“設計系統的架構受制于産生這些設計的組織的溝通結構。”
每個公司的組織架構不同,則會演進出不同的架構。這個很關鍵,後面會反複提到。
比如你的公司是垂直架構,那就是遞進關系,隻有一條線:後端--前端--測試--運維,這樣開發過來,最後找問題肯定是反向來的。
如果你的公司是水準架構,那就是分布式關系,比如:
遊戲部+視訊部+直播部----大後端(中台),那麼你的架構一定是分布式的,并且做了服務拆分,将公共邏輯做成了服務。
那麼,有銀彈麼?沒有!
就像阿裡為什麼要建立中台又拆?隻有不斷的業務改進和架構演進,一切都是為了更好地服務業務,減少開發成本。
是以:
那麼,我們來講講痛點。
二、單體架構,巨石架構
在開聊微服務之前,我先要你和介紹下單體應用。如果你不知道單體應用的痛,那也不會深刻了解微服務的價值。
當公司團隊規模很小的時候,單體應用是最好的選擇。主要模式有:LANMP(Linux + Apache或Nginx + MySQL + PHP)和MVC(Spring + iBatis/Hibernate + Tomcat)兩種。
其優點是學習成本低,開發上手快,測試、部署、運維也比較友善,甚至一個人就可以完成一個網站的開發與部署。
1-4人小團隊首選,放開膀子就是撸。
别上來就微服務,幾個開發人員的小公司,去追求微服務、去追求中台架構,這是追求完美嗎?不是,是找死。
單體架構的優點
-
開發簡單:
- 隻有一個git,下載下傳部署調試都很友善。
- 很容易看到全貌,所有的代碼都在這裡了。
-
易對應用程式進行大規模修改:
-
沒有服務間的聯調,沒有多級調用。測試相對簡單直覺:
-
隻有一個應用,扔上去執行就OK了。部署簡單明了:
-
直接運作多個執行個體,開負載均衡即可。橫向擴充不費吹灰之力:

單體架構的缺點
- 應用複雜,
。耦合性高
- 導緻了重構困難、難以靈活性開發,一崩則全崩。牽一發而動全身。
- 難以擴充,可能久了就誰都不敢動了。新同僚一來需要閱讀大量代碼,學習成本極高。如果相關負責人離職,那将是雪崩效應。
- 開發困難
-
,代碼都在一起,當然卡。IDE卡
-
。當單體應用的代碼越來越多,依賴的資源越來越多時,應用編譯打包、部署測試一次,甚至需要10分鐘以上。編譯慢、啟動慢
-
-
,隻能長期依賴某個可能已過時的技術棧。架構更新困難
- 更新就隻能全升,不能有針對性地利用語言優勢。
-
團隊協作開發成本高
- 早期在團隊開發人員隻有兩三個人的時候,協作修改代碼,最後合并到同一個master分支,然後打包部署,尚且可控。
- 但是一旦團隊人員擴張,超過5人修改代碼,然後一起打包部署,測試階段隻要有一塊功能有問題,就得重新編譯打包部署,然後重新預覽測試,所有相關的開發人員又都得參與其中,效率低下,開發成本極高。
- 如果分了團隊開發子產品,團隊之間的時間安排會不一緻,A團隊的代碼很可能對B團隊的代碼産生影響。
-
如果有一個大bug,記憶體洩漏、DB打爆,都是噩夢。無法隔離:
三、什麼是服務化
最開始的啟發來源于一本書《The Art of Scalability》,即《可擴充性的藝術》,探讨了現代企業的可擴充 Web 架構、流程群組織。
裡面提到了三種擴充方式:
分别是:
-
:單體+負載均衡(随機算法)多個執行個體的負載均衡
【微服務理論】微服務概覽 -
:單機+負載均衡(雜湊演算法)根據請求的屬性路由請求
【微服務理論】微服務概覽 -
:即服務。每個服務都是由一組專注的、内聚的功能職責組成。根據功能拆分業務
【微服務理論】微服務概覽
圍繞業務功能建構的,服務關注單一業務,服務間采用輕量級的通信機制,可以全自動獨立部署,可以使用不同的程式設計語言和資料存儲技術。
我們将單體應用中的邏輯分離,單獨成立一個項目,獨立開發、測試、上線、運維,然後将程序間調用變成遠端RPC調用,各個子產品之間不耦合,可以交給不同的團隊管理,還可以根據各個子產品的特點選擇不同的語言。這樣就可以解決單體應用膨脹、團隊開發耦合度高、協作效率低下的問題。
微服務1.0(SOA,面向服務的架構模式)
什麼東西過大,解決的最好辦法就是把它變小。
分而治之,化繁為簡。
在我看來,微服務也是分級别的。也就是微的程度。
比如可以先按大的邏輯拆分,用電商舉例,商品展示、購買、物流這樣分。
也可以商品服務、訂單服務、支付服務等這樣分。
- 縱向拆分:從業務次元進行拆分。比如可以先按大的邏輯拆分,用電商舉例,商品展示、購買、物流資訊。
- 橫向拆分:從公共且獨立功能次元拆分。标準是按照是否有公共的被多個其他服務調用,且依賴的資源獨立不與其他業務耦合。比如商品服務、訂單服務、支付服務、評價服務等。
【微服務理論】微服務概覽
差别是什麼?
前一種隻是按接口分,我把展示的接口都放到一個單體寫,那樣這個單體挂了,購買的人不受影響。
問題就是公共子產品(鑒權、底層方法)我要寫多套。
适用于中型團隊,因為公共子產品也不是天天改。
好處
最大程度将業務耦合性降低,A子產品挂了不影響B子產品,而且子產品可以配置設定給不同的人,單獨治理。
線下麻煩點,但是線上穩定點。
舉例:賬号子產品,此時可能是每個單體裡面都有一套賬号資料生成代碼,然後用同一個git維護,一旦更新了就所有單體都要更新。
盡管也是子產品化邏輯,但是最終它還是會打包并部署為單體式應用。
問題
- 代碼備援嚴重。
- 單機扛不住隻能上負載均衡,查日志可能要查多台機器。更新也是,要批量上代碼。
- 資料庫難以隔離,尤其是使用者這種量最多的資料,可能一個bug導緻全盤慢。
- 故障無法轉移,A機器挂了, B機器的代碼是一樣的,基本也會挂。
- 影響的是一個整體業務,無法降級,無法熔斷。挂就是一個業務挂。
微服務架構與SOA的異同
相似點:
- 都是特定的風格架構;
- 都以一系列服務方式組織系統;
不同點:
- 完全不同的技術棧(微服務架構采用輕量級、開源技術以及啞管道通信);
- 處理資料方式不同(微服務架構有自己的資料庫);
- 服務尺寸、規模不同(微服務架構中的服務相對較小);
- 通信方式不同,SOA采用了ESB企業總線之類的重量級通信方式。
微服務2.0
另一種就是按子產品分,或者叫按資料分,這才是真的微服務。
什麼是微服務
:圍繞業務功能建構的,服務關注單一業務,服務間采用輕量級的通信機制,可以全自動獨立部署,可以使用不同的程式設計語言和資料存儲技術。
重點:每個服務都有自己資料模型和資料庫。
隻有獨占DB,才能談隔離,才能有針對性地擴縮容、上叢集。
适用于大型公司,業務量很大,每個子產品都是一個小團隊負責。
最大程度減少溝通成本。
當人員數量上來後,最大的消耗并不是編碼時間,而是溝通時間。
舉例:我将賬号集中到一個服務,所有人需要賬号資訊都找我來拿,然後我這邊用負載均衡、叢集保證我的高可用,如果有人要更新使用者資料,要我走我的接口,要麼走我的消息隊列,我來維護賬号資料的一緻性。
圍繞業務或者資料建構,服務關注單一業務。服務間采用輕量級的通信機制,可以全自動獨立部署,可以使用不同的程式設計語言和資料存儲技術。微服務架構通過業務拆分實作服務元件化,通過元件組合快速開發系統,業務單一的服務元件又可以獨立部署,使得整個系統變得清晰靈活。
- 小即是美:小的服務代碼少,bug 也少,易測試,易維護,也更容易不斷疊代完善的精緻進而美妙。
- 單一職責:一個服務也隻需要做好一件事,專注才能做好。
- 一個服務隻做一件事
- 一個包隻做一件事
- 一個函數隻做一件事
- 錯誤例子:Common utils ,最後就隻是垃圾桶。
- 盡可能早地建立原型:盡可能早的提供服務 API,建立服務契約,達成服務間溝通的一緻性約定,至于實作和完善可以慢慢再做。
- 可移植性比效率更重要:服務間的輕量級互動協定在效率和可移植性二者間,首要依然考慮相容性和移植性。
微服務的問題是什麼?
Fred Brooks 在30年前寫道,
“there are no silver bullets”。
但凡事有利就有弊,微服務也不是萬能的。
- 服務的拆分與定義是一項挑戰;
- 沒有具體的、良好的算法可以完成拆分工作,這讓服務的拆分更像一門藝術。
- 如果拆分的不好,會成分布式單體,會包含單體和微服務兩者的弊端。
- 分布式系統帶來各種複雜性,使開發、測試和部署變得困難;
- 跨服務的事務和查詢。
- 資料一緻性。
- 需要高度自動化。
- 需要統一的日志采集。
- 當部署跨越多個服務的功能時需要謹慎地協調更多開發團隊;
- 需要根據服務依賴來制定開發計劃。
- 需要制定統一的通訊協定。
- 開發者需要思考到底應該在應用的什麼階段使用微服務架構;
- 初創公司應該從單體開始。
- 測試困難
- 不知道是代碼的bug還是環境的bug。
- 連鎖反應
- 上遊(調用者)寫了個for循環,給下遊(提供者),然後下遊再給他的下遊,可能就指數級放大了。
- 需要添加限流、熔斷等政策。
- 依賴關系複雜
- 服務都成了子產品化,彼此之間的依賴關系是極其混亂的,如果不用工具,很難維護調用關系。
常見問題案例
這樣帶來什麼問題
:
問題1:for循環點查,qps被成倍放大。一個請求到了下遊可能就成了100個。
問題2:串行請求,慢,而且不做降級就會直接逾時導緻500。
問題3:測試困難,不是代碼的問題,而是部署的問題。
問題4:維護困難
• 日志檢視,這麼多台機器(或目錄)。
• 聯調:不知道是哪個環節慢。
• 報警:哪一環被打挂了也不知道。
解決qps放大:
- 粗粒度API,批量傳回多個使用者的中繼資料。
- 并行請求,多協程請求多個服務節點,再組裝結果。
微服務的優勢
既然這麼多人選擇微服務,肯定是利大于弊的。
- 使大型的複雜應用程式可以持續傳遞和持續部署
- 自動化測試—-持續傳遞和持續部署
- 獨立部署
- 開發團隊自主且松散耦合
- 每個服務都相對較小并容易維護;
- IDE快,啟動快。
- 提高調試、部署等環節效率。
- 拆分後,代碼量少了, bug也更好發現。
- 減少了溝通成本,每個人維護好自己的服務即可。
- 服務可以獨立部署;
- 服務可以獨立擴充;
- 有針對地選擇伺服器。
- 微服務架構可以實作團隊的自治;
- 每個團隊可以有自己的技術和管理方法。
- 更容易實驗和采納新技術;
- 友善試錯和疊代。
- 更好的容錯性;
- 故障隔離了,程序和資料庫都隔離了。
- 耦合性極低。(去中心化)
- 資料去中心化: 獨占DB,減少資料幹擾。以前一個慢SQL打挂全服的情況不會有了。
- 技術去中心化:想用什麼語言都可以,隻要實作協定即可。便于重構和疊代。
- 治理去中心化:
-
:一個服務隻做一件事情。原子服務
-
:獨立程序、獨立機器、獨立釋出。獨立程序
-
:每個服務之間獨立部署,可以避免互相影響,并且和按需進行配置設定資源,節省成本 。隔離部署
-
四、實作微服務
微服務這麼好,我們該怎麼實作它呢?
元件服務化
傳統實作元件的方式是通過庫(library),庫是和應用一起運作在程序中,庫的局部變化意味着整個應用的重新部署。
通過服務來實作元件,意味着将應用拆散為一系列的服務運作在不同的程序中,那麼單一服務的局部變化隻需重新部署對應的服務程序。
我們用 Go 實施一個微服務:
- kit:一個微服務的基礎庫(架構)。
- service:業務代碼 + kit 依賴 + 第三方依賴組成的業務微服務
- rpc + message queue:輕量級通訊
本質上等同于,多個微服務組合(compose)完成了一個完整的使用者場景(usecase)。
基礎設施自動化
無自動化不微服務,自動化包括測試和部署。單一程序的傳統應用被拆分為一系列的多程序服務後,意味着開發、調試、測試、監控和部署的複雜度都會相應增大,必須要有合适的自動化基礎設施來支援微服務架構模式,否則開發、運維成本将大大增加
- 基建搭建好:消息隊列、日志采集、容器編排等。
容器化是微服務的基石
- 文檔寫好,gRPC代碼即文檔。
- 自動部署,CICD:Gitlab + Gitlab Hooks + k8s
- 染色釋出,友善開發和壓測。
- 監控體系:k8s,以及一系列 Prometheus、ELK、Conrtol Panle
- Testing:測試環境、單元測試、API自動化測試
可用性
著名的 Design For Failure 思想(所有依賴的東西都會崩)
微服務架構采用粗粒度的程序間通信,引入了額外的複雜性和需要處理的新問題,如網絡延遲、消息格式、負載均衡和容錯,忽略其中任何一點都屬于對“分布式計算的誤解”。
- 隔離
- 逾時控制
- 負載保護
- 限流
- 降級
- 重試
- 負載均衡
魯棒是Robust的音譯,也就是健壯和強壯的意思。 它也是在異常和危險情況下系統生存的能力。
比如說,計算機軟體在輸入錯誤、磁盤故障、網絡過載或有意攻擊情況下,能否不當機、不崩潰,就是該軟體的魯棒性。
所謂“魯棒性”,也是指控制系統在一定(結構,大小)的參數攝動下,維持其它某些性能的特性。
相容性
一旦采用了微服務架構模式,那麼在服務需要變更時我們要特别小心,服務提供者的變更可能引發服務消費者的相容性破壞,時刻謹記保持服務契約(接口)的相容性。
伯斯塔爾法則伯斯塔爾法則:
Be conservative in what you send, be liberal in what you accept.
發送時要保守,接收時要開放。
按照伯斯塔爾法則的思想來設計和實作服務時,發送的資料要更保守,意味着最小化的傳送必要的資訊,接收時更開放意味着要最大限度的容忍備援資料,保證相容性。
既然分了部門。語言、習慣等都會有很多不一樣,是以要多做相容,多寫文檔(gRPC代碼即文檔)。
所有依賴的東西都會崩。
五、微服務的政治
想要推行一種新的架構,都是困難的,畢竟誰也不想做改變。
銀彈現象:
過熱期(迷戀和崇拜)— 低谷期(失望)— 成熟期(生産力的高地,理性地使用)
理性地看待新技術。
騎大象的人:大象代表思維中情緒化的部分,它完成了絕大部分決策。騎大象的人代表理性的部分,用來對大象的決策進行判斷。
而架構的改變也必然會帶來組織架構的改變。
比如以前,後端開發隻是按功能大緻分一分,誰寫的就一直維護那一塊,然後寫完了就釋出測試,然後打包給運維上線。
但是一旦線上有問題的時候,隻能運維先看伺服器有沒有問題,沒有問題就開始搖後端負責人,然後負責人大緻看一下,看不出來就喊對應功能的負責人。
有的甚至連日志在哪裡看都不知道,畢竟不知道運維怎麼部署的。
現在轉為了微服務,大家自己負責這個子產品的開發、單元測試、運維部署、監控,自動化部署,日志采集也是統一的。
You build it , you run it
運維隻需要提供好用的工具,測試隻用黑盒測試,測使用者場景和UI。
這樣就形成了一個閉環團隊,而且可擴充性大大提升。
總結
沒有最好的架構,隻有最适合自己的架構。根據自己的業務、團隊來定制架構,能快速傳遞、快速疊代、持續重構的架構,就是好架構。
第一,要記住微服務不是解決所有問題的萬能"銀彈"。
第二,編寫整潔的代碼和使用自動化測試至關重要,因為這是現代軟體開發的基礎。
第三,關注微服務的本質,即服務的分解和定義,而不是技術,如容器和其他工具。
第四,確定你的服務松耦合,并且可以獨立開發、測試和部署,不要搞成分布式單體(Distributed Monolith),那将會是巨大的災難。
第五,也是最重要的,不能隻是在技術上采用微服務架構。擁抱 DevOps 的原則和實踐,在組織結構上實作跨職能的自治團隊,這必不可少。還必須記住∶ 實作微服務架構并不是你的目标。你的目标是加速大型複雜應用程式的開發。
當問題足夠大、有足夠多的不确定性因素時,人們習慣把大的問題拆分成小的問題,通過分割、抽象和重用小而可靠的功能子產品來建構整體的方案。
但是當這些小的、可重用的部分越來越多時,又會出現新的問題。
參考:
- 《微服務架構設計模式》