服務(wù)架構(gòu):微服務(wù)架構(gòu)
微服務(wù)架構(gòu)是由很多小而自治的微服務(wù)組合而成,每個(gè)服務(wù)都提供一套獨(dú)立自洽的業(yè)務(wù)能力。從應(yīng)用的層面,我們首先對(duì)業(yè)務(wù)進(jìn)行建模,業(yè)務(wù)域定義的邊界也自然而然作為每個(gè)微服務(wù)的職能邊界。
什么是微服務(wù)?
微服務(wù)的定義比較直白,但只有理解它的設(shè)計(jì)理念和原則,才能真正用好它。從單個(gè)服務(wù)/服務(wù)間關(guān)系來看,有以下特征:
- 微服務(wù)通常很小、提供很少但獨(dú)立的功能,服務(wù)間耦合度很低。一個(gè)很小的研發(fā)團(tuán)隊(duì)就可以開發(fā)和維護(hù)一個(gè)微服務(wù)。
- 每個(gè)服務(wù)都是一個(gè)單獨(dú)的代碼庫,可以由一個(gè)很小的研發(fā)團(tuán)隊(duì)來管理。
- 服務(wù)可以獨(dú)立部署,而不需要重新構(gòu)建和部署整個(gè)應(yīng)用。
- 每個(gè)服務(wù)負(fù)責(zé)維護(hù)自己的數(shù)據(jù)和狀態(tài),數(shù)據(jù)所有權(quán)歸本服務(wù)所有,其他服務(wù)必須通過該服務(wù)訪問數(shù)據(jù)和狀態(tài)。
- 服務(wù)之間通過定義好的API進(jìn)行通信,對(duì)外隱藏API的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。
- 支持多語言編程,不同服務(wù)可以使用獨(dú)立的技術(shù)棧進(jìn)行開發(fā),自由選擇編程語言、框架和庫。
如果從更全局的視角來看,微服務(wù)架構(gòu)還必須下面幾個(gè)組件:
- 服務(wù)管理和編排。該組件負(fù)責(zé)把服務(wù)部署在物理機(jī)/虛擬機(jī)節(jié)點(diǎn)上、做失敗檢查、在多個(gè)節(jié)點(diǎn)上負(fù)責(zé)對(duì)服務(wù)的多個(gè)實(shí)例做負(fù)載均衡等。我們通??梢允褂矛F(xiàn)成的開源技術(shù)來充當(dāng)這個(gè)組件,比如k8s。
- API Gateway。API Gateway給所有Client提供了訪問入口。Client通常不會(huì)直接訪問提供業(yè)務(wù)支持的服務(wù),而是調(diào)用API Gateway,由Gateway負(fù)責(zé)把請(qǐng)求轉(zhuǎn)發(fā)給后端對(duì)應(yīng)的服務(wù)。
使用 API Gateway 有很多優(yōu)勢(shì):
- 解耦Client和服務(wù),我們可以單獨(dú)對(duì)服務(wù)進(jìn)行更新或重構(gòu),client端可能不需要更新甚至不需要感知。
- 服務(wù)間通信可以使用更靈活的方式,比如不走HTTP協(xié)議,而是走rpc等二進(jìn)制協(xié)議、消息隊(duì)列等,某些超大數(shù)據(jù)量的場(chǎng)景甚至?xí)遬2p協(xié)議。
- API Gateway可以承擔(dān)一些跨業(yè)務(wù)的職能,比如權(quán)限認(rèn)證、日志、SSL校驗(yàn)、負(fù)載均衡等。
- API Gateway一般會(huì)提供一些開箱即用的能力,比如限流、緩存、請(qǐng)求篡改、權(quán)限校驗(yàn)等。
架構(gòu)優(yōu)勢(shì)
- 支持敏捷迭代。由于微服務(wù)可以獨(dú)立部署,所以修復(fù)bug和上線新特性也更容易。你可以獨(dú)立更新特定的服務(wù),而不是重新部署整個(gè)應(yīng)用,如果更新出現(xiàn)問題,也支持快速回滾。在傳統(tǒng)應(yīng)用中,一旦在應(yīng)用的某個(gè)環(huán)節(jié)發(fā)現(xiàn)問題,整個(gè)發(fā)布流程都要被阻塞。新特性也可以等著bug修復(fù)后再去測(cè)試和發(fā)布。
- 小而專的研發(fā)團(tuán)隊(duì)。微服務(wù)可以很小,小到一個(gè)獨(dú)立的小團(tuán)隊(duì)iu可以構(gòu)建、測(cè)試和阿布。小團(tuán)隊(duì)非常適合推行敏捷迭代流程。往往大型團(tuán)隊(duì)的生產(chǎn)力更弱一些,因?yàn)闇贤M,管理成本上升,由此也不適合再推行敏捷迭代。
- 代碼庫小。在大型單體應(yīng)用中,隨著時(shí)間推移,代碼依賴越來越復(fù)雜。增加新特性意味著要改很多地方的代碼。由于不共享代碼和數(shù)據(jù),微服務(wù)架構(gòu)將依賴最小化,部署新特性也更容易。
- 可以融合使用多種技術(shù)。團(tuán)隊(duì)成員可以選擇最適合服務(wù)的技術(shù),并在適當(dāng)?shù)那闆r下混合使用多個(gè)技術(shù)棧。
- 故障隔離。如果一個(gè)特定的微服務(wù)掛掉了,只要上游微服務(wù)在設(shè)計(jì)時(shí)考慮到容災(zāi)處理,通常不會(huì)影響整個(gè)應(yīng)用的正常功能。比如,采用適當(dāng)?shù)娜蹟鄼C(jī)制,或者服務(wù)間通信走異步消息隊(duì)列。
- 架構(gòu)擴(kuò)展性好。每個(gè)服務(wù)都可以單獨(dú)進(jìn)行擴(kuò)容,這樣的話,我們就能獨(dú)立擴(kuò)容需要更多資源的子系統(tǒng),而不是整個(gè)應(yīng)用。使用K8s等對(duì)服務(wù)進(jìn)行編排,我們可以把多個(gè)服務(wù)的實(shí)例部署在同一臺(tái)機(jī)器上,以提高資源利用率。
- 原生支持服務(wù)間的數(shù)據(jù)隔離。如果要更新數(shù)據(jù)的schema,只有單個(gè)服務(wù)受到影響。在單體應(yīng)用中,更新schema往往不太容易,因?yàn)閼?yīng)用的多個(gè)模塊都會(huì)通過庫依賴的形式訪問數(shù)據(jù),在更新時(shí),很難追蹤到所有使用的地方和使用方式,導(dǎo)致schema的迭代有很多未知的風(fēng)險(xiǎn)。
有哪些挑戰(zhàn)
微服務(wù)的架構(gòu)優(yōu)勢(shì)是有代價(jià)的,在采用微服務(wù)架構(gòu)之前,我們要認(rèn)識(shí)到未來可能面臨的挑戰(zhàn):
- 架構(gòu)復(fù)雜度高。相對(duì)于單體應(yīng)用,微服務(wù)應(yīng)用有很多可變的組件。每個(gè)服務(wù)都更簡單了,但整個(gè)系統(tǒng)變得更復(fù)雜了。
- 開發(fā)和測(cè)試麻煩,尤其是集成測(cè)試。在實(shí)現(xiàn)一個(gè)微服務(wù)時(shí),如果它依賴了其他服務(wù),編寫和測(cè)試過程都會(huì)比單體應(yīng)用更復(fù)雜?,F(xiàn)存的工具在設(shè)計(jì)時(shí)可能并未考慮到服務(wù)依賴。如果我們要對(duì)相互依賴的多個(gè)服務(wù)進(jìn)行重構(gòu),難度也會(huì)比重構(gòu)單體應(yīng)用高。在測(cè)試時(shí),涉及到服務(wù)依賴,需要單獨(dú)維護(hù)多套與線上相似的測(cè)試環(huán)境??紤]到每個(gè)服務(wù)都有多個(gè)測(cè)試版本,迭代比較快時(shí),很容易出問題。
- 互相獨(dú)立的服務(wù),很難做治理。由于微服務(wù)以區(qū)中心化的方式去構(gòu)建各個(gè)服務(wù),在服務(wù)治理上增加了額外的難度。最終整個(gè)應(yīng)用中可能包含多個(gè)編程語言,使用了多種框架,開發(fā)人員可以更自由地選擇小眾的技術(shù),導(dǎo)致后期維護(hù)成本反而更高。
- 容易產(chǎn)生網(wǎng)絡(luò)擁塞和延遲。很多小且功能獨(dú)立的服務(wù)需要通過API或消息隊(duì)列繼續(xù)頻繁的通信。由于底層走網(wǎng)卡進(jìn)行通信,對(duì)比單體架構(gòu)的代碼庫依賴,網(wǎng)絡(luò)通信的代價(jià)可能很高。當(dāng)服務(wù)的依賴鏈很長時(shí),比如 A調(diào)用B,B調(diào)用C,C調(diào)用...,額外的網(wǎng)絡(luò)延遲會(huì)高到無法接受。所以在設(shè)計(jì)服務(wù)時(shí),需要慎重考慮API的設(shè)計(jì),應(yīng)避免過度繁瑣的API,考慮到編碼格式(二進(jìn)制協(xié)議優(yōu)于文本協(xié)議),優(yōu)先使用異步通信模式或消息隊(duì)列模式;
- 跨服務(wù)的數(shù)據(jù)一致性問題。每個(gè)微服務(wù)負(fù)責(zé)自身的數(shù)據(jù)持久化,導(dǎo)致多個(gè)服務(wù)的數(shù)據(jù)可能存在不一致。通常我們使用最終一致性原則解決這些問題;
- 服務(wù)管理難度高。微服務(wù)架構(gòu)的成功采用離不開成熟的DevOps文化。處理跨服務(wù)的日志追蹤,尤其是一個(gè)用戶操作觸發(fā)整個(gè)鏈路的微服務(wù)調(diào)用,日志都要記錄下來,并且在發(fā)生錯(cuò)誤時(shí),能夠把整個(gè)調(diào)用鏈的日志拉取出來;
- 服務(wù)的發(fā)布版本管理。在更新服務(wù)時(shí),不能對(duì)API做破壞性的更新,否則依賴它的服務(wù)可能會(huì)掛掉。由于多個(gè)開發(fā)團(tuán)隊(duì)在同一時(shí)間段可能都要對(duì)服務(wù)做更新,所以接口設(shè)計(jì)必須保持向前或向后兼容。一個(gè)典型的低級(jí)錯(cuò)誤是:刪除了API定義的某些字段,或修改了某些字段的業(yè)務(wù)含義;
- 要求研發(fā)團(tuán)隊(duì)懂分布式系統(tǒng)。微服務(wù)通常是分布式的,需要認(rèn)真評(píng)估團(tuán)隊(duì)是否具備對(duì)應(yīng)的能力和經(jīng)驗(yàn);
最佳實(shí)踐
- 基于業(yè)務(wù)場(chǎng)景對(duì)服務(wù)進(jìn)行建模;
- 盡量去中心化。每個(gè)團(tuán)隊(duì)都要負(fù)責(zé)設(shè)計(jì)和構(gòu)建服務(wù),避免分享代碼或底層數(shù)據(jù)結(jié)構(gòu)。
- 數(shù)據(jù)存儲(chǔ)只允許單個(gè)服務(wù)訪問,每個(gè)服務(wù)根據(jù)自己的業(yè)務(wù)需求設(shè)計(jì)選擇存儲(chǔ)方案和數(shù)據(jù)格式。
- 服務(wù)間通過設(shè)計(jì)良好的API進(jìn)行通信,應(yīng)盡量避免透露實(shí)現(xiàn)細(xì)節(jié)。API的定義應(yīng)當(dāng)基于業(yè)務(wù)場(chǎng)景進(jìn)行設(shè)計(jì),而不是根據(jù)內(nèi)部的技術(shù)實(shí)現(xiàn)進(jìn)行設(shè)計(jì)。
- 避免服務(wù)間的耦合。服務(wù)間耦合的原因通常是把數(shù)據(jù)庫schema透傳給外部,或通信協(xié)議彈性比較差。
- 將跨業(yè)務(wù)域的需求交給API Gateway來處理,比如身份認(rèn)證、SSL驗(yàn)證。
- 保持API Gateway的純粹性,不摻入業(yè)務(wù)邏輯。API Gateway只處理和轉(zhuǎn)發(fā)client側(cè)的請(qǐng)求,不感知任何業(yè)務(wù)邏輯。否則API Gateway將成為一個(gè)依賴,導(dǎo)致服務(wù)間的耦合。
- 服務(wù)間應(yīng)該高內(nèi)聚、低耦合。應(yīng)該一起變化的邏輯應(yīng)該被打包到一個(gè)服務(wù)里,統(tǒng)一部署。如果他們分散在不同的服務(wù)里,這些服務(wù)將高度耦合在一起,一個(gè)服務(wù)的更新也將連鎖導(dǎo)致另一個(gè)服務(wù)的更新。兩個(gè)服務(wù)間繁瑣的通信是“高耦合低內(nèi)聚”的體現(xiàn)。
- 故障隔離。應(yīng)當(dāng)采取一定的恢復(fù)策略,以避免一個(gè)服務(wù)的故障引發(fā)連鎖反應(yīng)。