首先,我們來看看微服務的定義:微服務是一個界限明確、高度封裝、鬆耦合、可以獨立部署和獨立擴充套件的服務應用元件。如圖所示。微服務架構基於 SOA 和領域驅動設計(DDD)構建,其主要目的包含以下三個方面:開發的敏捷性、部署的便利性及明確的可延伸性。
其次,微服務和傳統的 SOA 有什麼異同:
微服務架構為了實現其敏捷特性,在 SOA 架構約束的基礎之上又新增了新的約束,微服務之間不能互相依賴,因此要求微服務能夠獨立部署、獨立擴充套件,微服務之間的依賴越少越好。
微服務中的一個服務只實現一個獨立的特性。
對於微服務而言,儘量不要為外部應用釋出程式碼級 API,可以通過服務呼叫或者事件解決依賴問題。
微服務中的服務之間最好通過非同步事件互動。
微服務中的每個服務都擁有自己獨立的資料。
另外,微服務架構的優點有很多:
鑑於以上優點,我們來看一下微服務的本質。微服務在本質上是一種服務實現模式,微服務可以實現一個應用中獨立的業務功能,而不是整個應用或者模組,因此,微服務其實是將單體應用的複雜性從程式內部轉移到了服務元件之間,這就對微服務抽象的粒度有一個比較高的權衡要求。
對於微服務的粒度,大家討論的最多,這也是實踐過程中經常遇到的令人糾結的問題。實際上,如果抽象粒度太細,就需要大量的服務編排來滿足業務場景,而大量使用服務編排就可能會導致微服務退回到 SOA 模式(服務編排是類似 BPM 或者 ESB 的系統);而如果服務粒度太粗,則不利於開發的敏捷性和部署的便利性。
微服務的主要目的是實現敏捷,微服務架構的主要構建方式是採用領域驅動設計,而領域驅動設計主要包括如下五個概念:
微服務的粒度並沒有一個明確的界限,也就是說「尺寸」是相對的。通常,微服務的粒度和對敏捷性的要求密切相關,往往粒度越細其敏捷度也越高,但是並不是所有的應用對敏捷的要求都那麼高,也就是說在微服務的設計和實現過程中,對於粒度的大小可以做適當的調整。對於想要採用微服務架構的團隊,首先要考慮如下幾個前提條件:
綜上所述,我們建議微服務的演進線路如圖所示。
我們在構建微服務體系的過程中,經歷了技術選型、技術驗證、引入開源實現及完全自研等一系列的過程,其中也走過不少彎路,現在回想起來,感觸良多,在這裡跟大家分享一下,希望能夠有所幫助。對於技術選型,我們不希望重複發明輪子,也不希望完全受制於開源的實現,所以在技術選型時遵循如下原則:
考察社群熱度、架構成熟度、學習曲線、可維護性,如圖所示。
儘量照顧現有服務的開發方式和框架的使用習慣,不對目前的研發團隊造成太大的衝擊。能夠給程式設計師提供何種能力?我們的期望是:
我們比較了可能用到的一些開源服務架構,並對它們的功能點進行了評估,如圖所示。
由於之前團隊採用 RESTEasy + Spring Boot 的方式實現服務,不希望對現有的系統和產品產生太大的影響,因此決定服務架構首先需要支援 REST 服務,又由於 Netflix 提供了比較全面的解決方案,並且 Spring Cloud 在遵循 cloud-native 原則的基礎上對 Netflix 進行了比較友好的封裝,因此初步的結論是可以基於 Spring Cloud 進行二次開發,封裝我們自己的微服務架構。
經過半年的實踐,我們發現 Netflix 技術棧的思想不錯,但是很多實現並不是特別完美,例如:
基於 Eureka 的服務註冊依然是中心化治理的方式,與傳統基於 ZooKeeper 或者 etctd/consul 的治理方式一樣,中心化的治理會給服務的部署帶來非常大的阻礙,需要對不同的環境設定不同的中心註冊伺服器,服務的版本管理及服務註冊資訊的同步也會帶來問題,更加糟糕的是,在某些服務不正常的情況下,使用者端需要進行大量的判斷,防止出現治理風暴等問題。
因此,我們後來果斷轉換思路,對之前的微服務架構進行了徹底的改造:
融數資料微服務架構的整體設計思想如圖所示。
該設計思想的具體說明如下。
融數資料微服務總體架構(Graeae)如圖所示。
融數資料微服務總體架構有如下特性。
服務提供者 Endpoint 基於責任鏈模式(如圖所示)對 gRPC 進行封裝,對遮蔽了 gRPC 框架的事件驅動採用同步呼叫方式,方便業務遷移。
Endpoint 封裝了腳手架工具,提供基於 ProtoBuf 的 IDL 介面定義語言,使用契約優先的方式定義服務,並可以自動生產伺服器端和使用者端的程式碼框架。
程式碼優先意味著實現簡單,能夠快速執行。問題也很明顯,可能和某個具體語言繫結,面對多語言環境,其打通成本較高。
契約優先的中立性提供了一箇中間橋樑,讓面向多語言成為了可能,基於契約的元資訊為後續治理和演進提供了入口點。缺點是需要引入契約語言的學習,並與多語言進行適配。
Endpoint 採用生命週期自管理的方式,提供容器化的生命週期管理 API 和相應的 SPI,方便擴充套件及與 DevOps 工具結合。
外部管理(如 Tomcat)讓使用者不用關注自身的起始、消亡,但帶來的不足是對生命週期的管理相對減弱、部署的依賴管理擴散。
程序自治可以加強其對自身生命週期的管理,高內聚,不將依賴擴散。在一定程度上能夠帶來部署的便利及不同部署環境的適應性(如雲環境)。為優雅關閉提供切入點,進一步增強對系統的可控性。
從對環境適應性和對生命週期的管理能力考慮,程序自治有著不可忽略的優勢。
Endpoint 將設定與程式碼分離,提供多種方式的設定覆蓋能力,使得改變設定無須重新部署。
設定和程式碼一起進行的優點是使開發變得簡單,但不足也很明顯,即面對不同的環境需要部署多套程式碼,複雜度增加。
設定和程式碼分離後的優點是真正做到了只部署一套程式碼。設定資訊按環境獨立設定,不受環境制約,可隨時調整。
整合 Spring Boot,提供自定義註解方式,能夠快速啟動服務,方便開發。
//服務提供方
public class SmsTemplateApplication {
public static void main(String[] args) {
new SpringApplicationBuilder().sources(SmsTemplateApplication.class)
.web(false).showBanner(false).run(args);
}
}
//服務實現
public class SmsTempletServiceImpl implements SmsTemplateService { @Autowired
private SmsTempletDao smsTempletDao;
@Transactional
public DeleteReply deleteById(IdRequest req) { return SmsTemplateReply.newBuilder().build(); }
}
利用 Proxy 部署和設定服務治理端點,進行分層治理,如圖所示。
用 Proxy 配合部署方式來相容 semantic versioning 的版本管理,版本格式有主版本號、次版本號、修訂號、版本號遞增同時需要備註,規則如下:
先行版本號及版本編譯資訊可以加到「主版本號.次版本號.修訂號」的後面,作為延伸。
融數資料 DevOps 體系主要解決的問題是有效地識別和管理後設資料,以便高效、自動化、高品質地將系統動態組裝並執行起來,該體系架構如圖所示。
下面結合圖對 DevOps 體系做幾點說明。
該體系中的服務元件=(可執行程式碼 + 設定)&(依賴 + 設定)&(基礎設施 + 設定),如圖所示。
從部署的角度講,無論包還是包的集合(往往是組成一個獨立業務域的獨立功能)都需要依靠版本才能進行整體部署,可以部署在不同的邏輯環境上,也可以部署在同一個邏輯環境上,如圖所示。
DevOps 平臺通過構建平臺將程式碼編譯成物理二進位制包,再使用後設資料對這個二進位制物理包進行描述,形成邏輯包,且將部署、依賴和二進位制包本身的後設資料統一儲存到後設資料服務中,最終通過統一環境管理平臺讀取後設資料,按需拉取相應的邏輯包對應的物理包,放置到目標環境的相應目錄下。之後通過程序管理呼叫相應的服務啟動相關微服務,在部署的過程中,由邏輯環境管理系結 VIP 到微服務的 Proxy 上,將資訊註冊到 service inventory 服務上,這樣 Proxy 和服務 Endpoint 等的執行時資訊(例如 IP、埠、版本等)就可以被收集到 service inventory 服務上。
在真正呼叫服務時通過內建的 Zipkin 可以收集服務呼叫鏈的情況,並同 inventory 服務的後設資料資訊進行匹配,便可以準確地知道服務呼叫的關係,從而達到真正的分散式治理;而使用者端呼叫只需要知道服務所在的邏輯環境資訊就可以自動完成服務定址,這個服務地址就是 Proxy 繫結的 VIP 地址,從而簡化使用者端呼叫。DevOps平臺要做的就是保證 Proxy 的健壯性,由於 Proxy 只是簡單的反向代理,不儲存服務狀態,因此只需要做故障漂移就可以了,如圖所示。
要想成功地實施微服務架構,僅僅有服務架構、開發平臺及 DevOps 平臺是不夠的,組織和文化也需要適應微服務的需求。根據康威定律,架構由組織決定,因此需要對團隊的文化及組織劃分結構進行調整。
融數根據 two-pizza team 的原則,按照業務來劃分研發團隊,建立全棧小團隊,從而提高溝通效率、降低溝通成本,具體的團隊策略如圖所示。
將團隊切分後,我們按照業務線對組織進行架構規劃,以便技術團隊能夠專注於解決對應業務問題(業務驅動,或者說業務優先)。這時,團隊內部的設計決策將在團隊內部消化,因為團隊的規模已經是 7+/-2 人的量級,因此一般情況下不會對於團隊內成員來說過於複雜的工作。但團隊增多後,團隊的協調將是一個問題,因此微服務從技術上幫助團隊將負責的系統解耦,而計劃流程會幫助團隊在工作安排上找到合理的步驟。
那麼,雖然當一個大的業務被分解到各個小團隊時,還是會有跨團隊的設計工作,但是以上兩點是要嚴格執行的。技術團隊和業務團隊的合作並非經由上層協調,雙方的主要溝通都是團隊之間直接進行水平溝通,也就是說,在底層的團隊之間,需求、問題和日常交流都直接由業務團隊反饋給技術團隊的經理,而解決問題時容許業務團隊直接接觸開發人員,如圖所示。
我們在團隊中強調以結果為導向的工作方式,如圖所示。
團隊成員要有主人翁意識。團隊的利益就是個人的利益,團隊的成功才是個人的成功,團隊成員之間要相互幫助和補位,不允許存在「事不關己,高高掛起」的情況出現。
在團隊中,成員可以發表意見,也可以抱怨,但是不能只停留在語言層面,大家需要行動起來,找到解決問題的方法;我們希望每個開發人員都參與架構設計,而不是僅由架構師決定;我們還強調不要過度設計,鼓勵通過抽象和簡化解決問題,當然也鼓勵引入新技術,但前提是能夠有足夠的掌控力而且不影響系統穩定。
軟體工程師負責需求調研、設計、開發、測試、部署、維護、監控、功能升級等一系列的工作,也就是說軟體工程師負責應用或者服務的全生命週期的所有工作。運維是團隊成員的第一要務。在強大的自動化運維工具的支撐下,軟體工程師必須負責服務或者應用的 SLA。
在團隊中引入 oncall 機制,強調保障處理及時性,並引入卓越運維的思想,用資料統計每個團隊的線上故障、解決及時性,並逐步要求故障數量遞減,通過強大的資料支援保證團隊解決問題的 SLA,並逐步改善軟體品質。
DevOps 的推行是按業務來組織團隊,團隊包含設計、開發、測試、運維等人員,這樣一方面可以有效減少服務內部修改所產生的內耗;另一方面,團隊邊界可以變得更為清晰。DevOps 實際是一種文化上的變遷,打破了傳統開發與運維之間的壁壘,幫助組織形成從開發、測試到部署、運維這樣一個全功能化的高效能團隊。
來源:技術瑣話,內容選自《架構寶典》
作者:王東
王東,曾任融數資料北京研發中心 CTO,負責微服務、DevOps 以及巨量資料平臺的研發和管理工作。曾供職於 IBM、普元、Amazon、OneAPM 等國內外知名公司。擁有 15 年以上的 JavaEE 程式設計和架構設計經驗,精通 DevOps 和微服務,曾領導設計和開發普元 ESB 產品。熟悉支付相關的業務流程以及各個銀行和支付機構的業務處理模式,熟悉應用與支付領域的大規模分散式系統設計和開發方法,熟悉電子商務行業的業務模型。專注於客戶行為分析、行銷、產品和服務、客戶關係、線上銷售、物流配送、渠道整合、供應鏈整合、售後服務、預測和推薦等各個電子商務主要環節的分析和設計,以及 IT 系統的規劃和實施。精通 DDD、Scrum 等軟方開發和設計方法論。
4月,【冬哥有話說】DevOps之庖丁解牛,拆解DevOps的工具及具體實戰。公眾號留言「解牛」可獲取地址