天天看點

我對雲原生軟體架構的觀察與思考

我對雲原生軟體架構的觀察與思考

作者 | 易立,阿裡雲資深技術專家,容器技術負責人

本系列文章:

第一篇 - 雲原生基礎設施 (已釋出,文末點選閱讀原文檢視)

第二篇 - 雲原生軟體架構(本文)

第三篇 - 雲原生應用傳遞與運維體系(待續)

前言

在《雲原生基礎設施》一文中我們談到了,雲原生計算包含三個次元的内容,雲原生基礎設施,軟體架構和傳遞與運維體系,本文将聚焦于軟體架構層面。

“Software architecture refers to the fundamental structures of a software system and the discipline of creating such structures and systems. ” - 維基百科。

在我的了解,軟體架構主要目标是解決下列挑戰:

1、控制複雜性。由于業務的複雜性,需要我們用更好的手段幫助研發組織克服認知障礙,更好的分工協作。分而治之,關注點分離等手段皆是如此。

2、應對不确定性。業務在快速發展,需求在不斷變化。即使再完美的軟體架構,然而随着時間的推移,團隊的變化,軟體架構的調整不可避免。讀《設計模式》,《微服務設計》等書字裡行間寫的都是“解耦”兩字,讓我們關注架構中确定性和不确定性的分離,提升架構的穩定性和應變能力。

3、管理系統性風險。管理系統中的确定性以及不确定性風險,規避已知陷阱,對未知的風險做好準備。

雲原生應用架構的目标是建構松耦合、具備彈性、韌性的分布式應用軟體架構,可以更好地應對業務需求的變化和發展,保障系統穩定性。本文将分享我在這個領域的觀察和思考。

緣起 - 12要素應用

2012 年,Heroku 創始人 Adam Wiggins 釋出十二要素應用宣言。它為建構一個優雅的網際網路應用,定義了需要遵循的一些基本原則和方法論,也廣泛影響了衆多的微服務應用架構。十二要素重點關注:應用程式的健康成長,開發者之間的有效的協作,以及避免軟體架構腐化的影響。其内容在今天也值得每個同學認真體會。

我對雲原生軟體架構的觀察與思考

圖檔來源:

https://12factor.net/zh_cn/

12 要素應用為我們提供了很好的架構指導,幫助我們:

1、建構水準伸縮的彈性應用架構,更好支撐網際網路規模應用。

2、提升研發流程的标準化、自動化水準,提升研發效率。

3、減少開發環境和生産環境的差異,并使用持續傳遞實施靈活開發。

4、提升應用的可移植性,适合雲化部署,降低資源成本和管理複雜性。

松耦合架構設計

微服務的核心理念是,系統中的各個服務可被獨立開發、獨立部署,獨立更新,各個服務之間是松耦合的。雲原生應用架構理念是進一步強調架構的松耦合,降低服務之間互相依賴的程度。

API 優先的應用架構設計

在面向對象的軟體架構中,最重要的是定義對象以及對象的接口契約。SOLID 原則是最被人廣為熟知的設計原則。

Single responsibility principle - 單一職責原則

Open/closed principle - 開放/封閉原則

Liskov substitution principle - 裡氏替換原則

Interface segregation principle - 接口隔離原則

Dependency inversion principle - 依賴翻轉原則

将以上五個原則的英文首字母拼在一起就是 SOLID 原則,這也是幫助我們建構高内聚,低耦合、具備柔性的應用架構。在分布式微服務應用架構中,API優先是契約優先(Contract First)的自然拓展。

API 應該是被優先設計的:我們知道使用者需求是複雜多變的,比如從桌面到移動端,應用的展現方式和操作流程都有可能不同;然而業務邏輯的概念模型和服務互動是相對穩定的。相對而言,API 的接口是更加穩定的,而具體的實作是可以疊代實作和持續變化的。定義良好的 API 可以更好保障應用系統的品質。

API 應該是聲明式,可描述/自描述的:通過規範化的描述,API 易于溝通、易于了解、易于驗證,簡化開發協同。支援服務的消費者和提供者并行開發,加速開發周期。支援不同的技術棧的實作,比如對于同一個 API 接口,其服務實作采用 Java 。前端應用可以使用 JavaScript ,而伺服器端應用可以使用 Golang 進行服務調用等等。這樣可以讓開發組織可以根據自己的技能棧和系統要求靈活選擇合适的技術。

API 應該具備 SLA :API 作為服務間的內建界面,與系統的穩定性息息相關。SLA 應該作為 API 設計的一部分,而不是部署後再考慮。在分布式系統中,穩定性風險無處不在,通過 API 優先的設計模式,我們對獨立的服務進行穩定性架構設計、容量規劃;我們還可以對獨立的 API 進行故障注入、穩定性演練,來消除系統性的穩定性風險。

在 API 領域,最重要的趨勢是标準化技術的崛起。gRPC 是 Google 開源的的高性能、通用的、平台無關的 RPC 架構。它采用分層設計,其資料交換格式基于 Protobuf (Protocol Buffers) 協定開發,具備優秀的序列化/反序列化效率,也支援衆多開發語言。在傳輸層協定, gRPC 選擇了 HTTP/2,相較于 HTTP/1.1,其傳輸效率有了很大提升。此外 HTTP/2 作為一個成熟的開放标準,具備豐富的安全、流控等能力,同時擁有良好的互操作性。gRPC 不僅可以用于 Server 端服務調用,也可以支援浏覽器、移動 App 和 IoT 裝置與後端服務的互動。gRPC 在功能上已經具備完整的 RPC 能力,也提供了擴充機制來支援新的功能。

在 Cloud Native 的潮流下,跨平台、跨廠商、跨環境的系統間互操作性的需求必然會催生基于開放标準的 RPC 技術,而 gRPC 順應了曆史趨勢,得到了越來越廣泛地應用。在微服務領域, Dubbo 3.0 宣布了對 gRPC 協定的支援,未來我們也會看到更多的微服務架構基于 gRPC 協定開發,并提供良好的多語言支援。此外,在資料服務領域,gPRC 也成為一個優秀的選擇,大家可以參考 Alluxio的文章:

https://www.alluxio.io/blog/moving-from-apache-thrift-to-grpc-a-perspective-from-alluxio/

此外在 API 領域 Swagger (OpenAPI規範),GraphQL 都是大家值得關注的開放标準。大家根據自己的業務訴求靈活選用,本文不再贅述。

Event Driven Architecture 的崛起

談事件驅動架構 (EDA - Event Driven Architecture),我們首先來解釋一下什麼是事件。事件是指對已經發生的事情、狀态變化等的記錄。它們是不可變的(無法更改或删除),并且按其建立順序排序。相關各方可以通過訂閱已釋出的事件來擷取有關這些狀态變化的通知,然後使用所選擇的業務邏輯根據這些資訊采取操作。

事件驅動架構是一種建構松耦合的微服務系統的架構方式,微服務之間通過異步事件通信來進行互動。

事件驅動架構實作了事件的生産者和消費者的徹底解耦。生産者無需關注事件如何被消費,同時消費者無需關注事件的生産方式;我們可以動态添加更多消費者而不影響生産者,可以增加消息中間件對事件進行動态路由和轉換。這還意味着事件的生産者和消費者沒有時序上的依賴,即使由于應用當機無法及時處理消息,在重新恢複後,程式可以繼續從消息隊列中擷取這些事件繼續執行。這樣的松耦合架構,為軟體架構提供更高的靈活性、靈活性和健壯性。

事件驅動架構的另一個重要優點是提升了系統的可伸縮性。事件生産者在等待事件消費時不會被阻塞,并且可以采用 Pub/Sub 方式,讓多個消費者并行處理事件。

事件驅動架構還可以完美地與 Function as a Service (FaaS) 相整合。事件觸發函數執行業務邏輯,在函數中也可以編寫內建多個服務的“膠水代碼”,簡單、高效地建構事件驅動架構的應用。

但是 EDA 架構依然存在很多挑戰。

1、分布式的松耦合架構大大增加了應用基礎設施的複雜性。基于雲的部署傳遞方式和雲服務(消息隊列、函數計算服務等)可以使得該架構的穩定性,性能和成本效益進一步提高。

2、與傳統同步處理方式相比,異步事件處理存在與事件排序、幂等性、回調和異常處理相關的要求,整體設計難度更大一些。

3、在大多數情況下,由于缺乏跨多個系統的分布式事務支援,維護資料一緻性是非常具有挑戰性的。開發者可能需要權衡可用性和一緻性之間的關系。比如通過Event Sourcing(事件溯源)實作最終一緻性,詳情:

https://martinfowler.com/eaaDev/EventSourcing.html

4、互操作性。在現實世界中,事件無處不在,然而不同生産者對事件的描述卻不盡相同。開發者希望無論事件是從哪裡發出,都能夠以一緻的方式建構事件驅動的應用程式。CloudEvents(

https://cloudevents.io/

) 是一種以通用、一緻的方式描述事件資料的規範,由 CNCF Severless 工作組提出,提升了事件驅動應用的可移植性。目前,阿裡雲 EventBridge、Azure Event Grid 等事件進行中間件,以及 Knative Eventing ,阿裡雲函數計算等 FaaS 技術已經提供了對 CloudEnvents 的支援。

由于 EDA 自身架構的優點,在網際網路應用架構,業務資料化和智能化、IoT等場景有非常廣闊的前景。關于 EDA 的架構讨論,不在此繼續展開。

面向傳遞的應用架構

在雲原生軟體架構中,我們在設計階段不隻是關注軟體如何被建構,也需要以終為始。關注如何合理設計和實作軟體,才可以被更好地傳遞和運維。

應用和運作環境解耦

在 12 要素應用中,應用和運作環境解耦就已經被提出。而 Docker 容器的出現則進一步加強了這個理念。容器是一種輕量化的應用虛拟化技術,容器之間共享作業系統核心,支援秒級啟動,Docker 容器鏡像是一個自包含的應用打包格式,将應用和其依賴(如系統庫、配置檔案)等打包在一起,在不同環境保持部署一緻性。

容器可以作為 Immutable Infrastructure (不可變基礎設施)的基礎,提升應用傳遞的穩定性。不可變基礎設施是由 Chad Fowler 于 2013 年提出的構想:在這種模式中,任何基礎設施的執行個體(包括伺服器、容器等各種軟硬體)一旦建立之後便成為一種隻讀狀态,不可對其進行任何更改。如果需要修改或更新某些執行個體,就是建立一批新的執行個體進行替換。這種模式的可以減少了配置管理工作的負擔,保障系統配置變更和更新可以可靠地重複執行,避免令人頭疼的配置漂移問題;易于解決部署環境間的差異,讓持續內建與持續部署過程變得更流暢;支援更好的版本管理,在部署出錯時可進行快速復原,

我對雲原生軟體架構的觀察與思考

Kubernetes 作為容器的分布式編排排程系統,進一步提升了容器應用的可移植性。K8s通過一系列抽象如 Loadbalance Service, Ingress, CNI, CSI,幫助業務應用可以屏蔽底層基礎設施的實作差異,靈活遷移。通過這樣的能力,我們可以實作工作負載在資料中心、邊緣計算和雲環境的動态遷移。

在應用架構中,我們需要避免将靜态環境資訊,比如IP,mac位址等與應用邏輯耦合。在微服務架構中,可以利用 Zookeeper/Nacos 等實作服務的注冊發現;在 Kubernetes 中,我們可以通過 Service,Service Mesh 減少對服務端點IP的依賴。此外,對應用狀态的持久化也盡可能通過分布式存儲或者雲服務等實作,這樣可以大大提升應用架構可伸縮性和自愈能力。

自包含可觀測性

分布式系統所面對的最大挑戰之一就是可觀測性。可觀測性可以幫助我們解系統目前的狀态,并作為應用自愈,彈性伸縮和智能運維的基礎。

在雲原生架構中,微服務應用是自包含的,應該自己具備可觀測性,可以友善地被系統進行管理和探查。首先是,應用應該具備自身健康狀态的可視化能力。

在 Kubernetes 中,業務應用可以提供一個 liveness 探針,可以通過 TCP、HTTP 或者指令行方式對應用就緒進行檢測。對于 HTTP 類型探針,Kubernetes 會定時通路該位址,如果該位址的傳回碼不在 200 到 400 之間,則認為該容器不健康,會殺死該容器重建新的容器;

我對雲原生軟體架構的觀察與思考

對于啟動緩慢的應用,為了避免在應用啟動完成之前将流量導入。Kubernetes 支援業務容器提供一個 readiness 探針,對于 HTTP 類型探針,Kubernetes 會時通路該位址,如果該位址的傳回碼不在 200 到 400 之間,則認為該容器無法對外提供服務,不會把請求排程到該容器;

我對雲原生軟體架構的觀察與思考

同時在新的微服務架構中已經内置了可觀測探針,比如在 SpringBoot 的 2.3 釋出了兩個新的 actuator 位址,/actuator/health/liveness 和 /actuator/health/readiness ,前者用作存活探針,後者用作就緒探針。業務應用可以通過Spring系統事件機制來讀取、訂閱、修改 Liveness State 和 Readiness State ,這樣可以讓 Kubernetes 平台可以做更加準确的自愈和流量管理。

更多資訊可以參考 :

https://spring.io/blog/2020/03/25/liveness-and-readiness-probes-with-spring-boot

此外,應用可觀測性包含三個關鍵能力:日志、監控與鍊路追蹤。

我對雲原生軟體架構的觀察與思考

1、Logging – 日志(事件流):用于記錄離散的事件,包含程式執行到某一點或某一階段的詳細資訊。不但包括應用、 OS 執行過程的日志,還應包含運維過程中的日志資訊,如操作審計等。

2、Metrics – 監控名額:通常是固定類型的時序資料,包括 Counter、Gauge、Histogram 等,是可聚合的資料。系統的監控能力是多層次的,既包含計算、存儲,網絡等基礎設施服務層次的監控名額,也應該包含業務應用的性能監控和業務名額監控。

3、Tracing – 鍊路追蹤 - 記錄單個請求的完整處理流程,可以為分布式應用的開發者提供了完整的調用鍊路還原、調用請求量統計、應用依賴分析等能力,能夠幫助開發者快速分析和診斷分布式應用架構下的性能和穩定性瓶頸。

在分布式系統中,穩定性、性能、安全等問題可能發生在任何地方,需要全鍊路可觀測性能力保障,需要覆寫基礎設施層、 PaaS 層,應用等不同層次,并且可以在不同系統間實作可觀測性資料的關聯、聚合、查詢和分析。

軟體架構的可觀測領域具備廣闊的前景,也湧現出衆多的技術創新。2020 年 9 月 CNCF 釋出了雲原生可觀測性的技術雷達:

https://www.cncf.io/blog/2020/09/11/cncf-end-user-technology-radar-observability-september-2020/
我對雲原生軟體架構的觀察與思考

其中,Prometheus 已成為企業首選的雲原生應用程式的開源監控工具之一。Prometheus 培養了一個活躍的開發者和使用者社群。在 Spring Boot 應用架構中,通過引入 micrometer-registry-prometheus 的依賴,既可以讓應用的監控名額被 Prometheus 服務所采集。更多資訊可以參考文檔:

https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html

在分布式追蹤領域,OpenTracing 是 CNCF 下屬的開源項目。它是一個技術中立的分布式追蹤的規範,提供統一接口,可友善開發者在自己的服務中內建一種或多種分布式追蹤的實作。Jaeger 是Uber 開源的分布式追蹤系統,相容 OpenTracing 标準,已經成功在 CNCF 畢業。此外OpenTelemetry是一個潛在的标準,它試圖在融合 OpenTracing 和 OpenCensus 這兩個項目,形成統一的技術标準。

對于很多遺留的業務系統,現有應用并不具備完備的可觀測性能力。新興的服務網格技術可以成為提升系統可觀測性的新方式。通過資料平面代理的請求攔截,網格可以擷取服務間調用的性能名額。此外,在服務調用方應用中隻需加入需要轉發的消息 header,在服務網格上即可獲得完整的鍊路追蹤資訊。這樣的方式極大簡化了可觀測性能力的建設,可以讓現有的應用低成本融入雲原生可觀測性體系中。

阿裡雲提供了豐富的可觀測性能力。XTrace分布式追蹤提供了對 OpenTracing/OpenTelemetry 标準的支援。ARMS 提供了托管 Prometheus 服務,可以讓開發者無需關注系統的高可用和容量挑戰。可觀測性是 AIOps 的基礎,在未來企業IT應用架構中将扮演更加重要的角色。

面向失敗的設計 - Design For Failure

根據”墨菲定律“ — “Anything that can go wrong will go wrong”。分布式系統可能受到硬體、軟體等因素、或者内部和外部的人為破壞。雲計算比自建資料中心提供了更高SLA、更加安全的基礎設施,但是我們在應用架構設計時依然要時刻關注系統的可用性,關注潛在的”黑天鵝“風險。

系統化的穩定性需要在軟體架構,運維體系群組織保障等方面全局考慮。在架構層面,阿裡經濟體有着非常豐富的經驗,比如防禦式設計、限流、降級、故障隔離等,而且也向社群貢獻了Sentinel、ChaosBlade等優秀的開源項目。

本文,我會談談幾個在雲原生時代可以進一步思考的地方。我的總結是 “Failures can and will happen, anytime, anywhere. Fail fast, fail small, fail often and recover quickly.”

首先是“Failures can and will happen”,我們需要提升伺服器的可替換性。在業界有一個非常流行的隐喻:“Pets vs. Cattle”,寵物和家畜。我們面對一個架構選擇:對于應用所在伺服器我們是需要精心伺候,防止系統當機,出現問題後不惜一切代價搶救 (Pet);還是傾向于出現問題後,可以通過簡單抛棄和替代進行恢複(Cattle)。雲原生架構的建議是:允許失敗發生,確定每個伺服器,每個元件都能夠在不影響系統的情況下發生故障并且具備自愈和可替代能力。這個設計原則的基礎是應用配置和持久化狀态與具體運作環境的解耦。Kubernetes 的自動化運維體系讓伺服器的可替換性變得更加簡單。

此外是 “Fail fast, fail small, recover quickly” 。立即失效(Fail fast)是一個非常反直覺的設計原則,它背後的哲學是既然故障無法避免,問題越及早暴露、應用越容易恢複,進入生産環境的問題就越少。采用了 Fail-fast 政策以後,我們的關注點将從如何窮盡系統中的問題轉移到如何快速地發現和優雅處理失敗。隻要跑的夠快,故障就追不上我。:-) 在研發流程上,通過內建測試盡可能在早期發現應用存在的問題。在應用級别,可以采用斷路器(Circuit Breaker)等模式防止一個依賴服務的局部故障引起全局問題;此外通過 K8s 的健康監測、可觀測性可以實作對應用故障的探知,通過服務網格的斷路器功能,可以将故障發現、流量切換和快速自愈這些能力外置到應用實作之外,由系統能力保障。Fail small的本質在于控制故障的影響範圍——爆炸半徑。這個原則在架構設計和服務設計上都需要我們持續關注。

最後是“Fail often”,混沌工程是一種在生産環境周期性引入故障變量,驗證系統對非預期故障防禦的有效性的思想。Netflix 引入混沌工程概念解決微服務架構的穩定性挑戰,也得到了衆多網際網路公司的廣泛應用。在雲原生時代又有了更多新的手段,Kubernetes 讓我們可以輕松注入故障,殺死pod,模拟應用失效和自愈過程。利用服務網格我們可以對服務間流量進行更加複雜的故障注入,比如 Istio 可以模拟緩慢響應、服務調用失敗等故障場景,幫助我們驗證服務間的耦合性,提升系統的穩定性。

更多關于傳遞和運維架構的更多穩定性思考,我們會在下一篇文章中和大家分享。

應用基礎設施能力下沉

雲原生軟體架構的重要目标讓開發者關注業務邏輯,讓平台去承載系統複雜性。雲原生計算重新定義了應用與應用基礎設施的邊界,進一步提升了開發效率,降低了分布式應用開發的複雜性。

服務治理能力與業務邏輯解耦

在微服務時代,以 Spring Cloud 與 Apache Dubbo 為代表的應用架構取得了巨大的成功,它們通過代碼庫方式提供了服務通信、服務發現和服務治理能力(流量轉移、熔斷、限流、全鍊路追蹤等)。這些代碼庫被建構在應用程式本身中,随着應用一起釋出和維護。這樣的架構存在一些無法回避的挑戰。

1、侵入性:服務治理本質是橫向的系統級關注,是與業務邏輯正交的。但在現有微服務架構中,其實作方式和生命周期與業務邏輯耦合在一起的。服務治理能力的增強需要微服務架構的更新,會導緻整個系統所有元件的重新建構和部署,導緻更新和維護成本提升。

2、實作綁定:由于微服務架構代碼庫通常由特定語言實作,難以支援多語言(polyglot)實作。随着業務的快速發展,異構系統之間的內建逐漸成為挑戰。

我對雲原生軟體架構的觀察與思考
https://philcalcado.com/2017/08/03/pattern_service_mesh.html

為了解決上述挑戰,社群提出了 Service Mesh(服務網格)架構。它将業務邏輯與服務治理能力解耦。下沉到基礎設施,在服務的消費者和提供者兩側以獨立程序的方式部署。這樣既達到了去中心化的目的,保障了系統的可伸縮性;也實作了服務治理和業務邏輯的解耦,二者可以獨立演進不互相幹擾,提升了整體架構演進的靈活性;同時服務網格架構減少了對業務邏輯的侵入性,降低了多語言支援的複雜性。

Google、IBM、Lyft 主導發起的 Istio 項目就是服務網格架構的一個典型的實作,也成為了新的現象級“網紅”項目。

我對雲原生軟體架構的觀察與思考

上圖是Istio的架構,邏輯上分為資料平面和控制平面。資料平面負責服務之間的資料通信。應用和以 sidecar 方式部署的智能代理 Envoy 成對出現。其中由 Envoy 負責截獲和轉發應用網絡流量,收集遙測資料并且執行服務治理政策。在最新的架構中, istiod 作為控制平面中負責配置的管理、下發、證書管理等。Istio 提供了一系列通用服務治理能力,比如:服務發現和負載均衡,漸進式傳遞(灰階釋出),混沌注入與分析,全鍊路追蹤,零信任網絡安全等。可以供上層業務系統将其編排到自己的IT架構和釋出系統之中。

服務網格在架構上實作了資料平面與控制平面的分離,這是一個非常優雅的架構選擇。企業客戶對資料平面有着多樣化的需求,比如支援等多樣化協定(如Dubbo),需要定制化的安全政策和可觀測性接入等。服務控制平面的能力也是快速變化的,比如從基礎的服務治理,到可觀測性,到安全體系,穩定性保障等等。但是控制平面與資料平面之間的API是相對穩定的。

CNCF 建立了通用資料平面 API 工作組(Universal Data Plane API Working Group / UDPA-WG),以制定資料平面的标準 API。通用資料平面 API(UDPA)的目标是:為 L4/L7 資料平面配置提供實作無關的标準化 API,類似于 OpenFlow 在 SDN 中對 L2/L3/L4 所扮演的角色。UDPA API 涵蓋服務發現、負載均衡、路由發現、監聽器配置、安全發現、負載報告、運作狀況檢查委托等。

UDPA API 基于現有的 Envoy xDS API 逐漸演進,目前除支援 Envoy 之外,将支援用戶端負載均衡實作 (比如 gRPC-LB),更多資料平面代理,硬體負載均衡和移動用戶端等等。

我們知道 Service Mesh 不是銀彈,其架構選擇是通過增加一個服務代理來換取架構的靈活性和系統的可演化性,但是也增加了部署複雜性(sidecar管理)和性能損失(增加兩跳)。UDPA 的标準化和發展将給服務網格架構帶來的新一次變化。

gRPC 在最新版本中提供了對UDPA負載均衡的初步支援:

https://github.com/grpc/proposal/blob/master/A27-xds-global-load-balancing.md

”proxyless “服務網格概念浮出水面,一個概念示意圖如下:

我對雲原生軟體架構的觀察與思考

gRPC 應用直接從控制平面擷取服務治理的政策, gPRC 應用之間直接通信無需額外代理。這個可以看到開放的服務網格技術的雄心,進化成為一套跨語言的服務治理架構,可以兼顧标準化、靈活性與運作效率。Google 的托管服務網格産品已經率先提供了對 ”proxyless“ gRPC 應用的支援。

新一代分布式應用運作時

對于分布式應用,Bilgin Ibryam 在Multi-Runtime Microservices Architecture :

https://www.infoq.com/articles/multi-runtime-microservice-architecture/

,文中分析并總結了典型的四大類需求:

生命周期(Lifecycle)

網絡(Networking)

狀态(State)

捆綁(Binding)

我對雲原生軟體架構的觀察與思考

熟悉傳統企業架構的同學可能發現,傳統的 Java EE (現在改名為 Jakarta EE )應用伺服器的目标也是解決類似的問題。一個典型 Java EE 應用伺服器的架構如下圖所示:應用生命周期由各種應用容器管理,如 Web 容器,EJB 容器等。應用的安全管理、事務管理、連接配接池管理都是交給應用伺服器完成。應用可以通過 JDBC 、JMS 等标準 API 接口通路外部的企業中間件,如資料庫、消息隊列等。

不同的外部中間件通過 Java Connector Architecture 規範實作與應用伺服器的插拔。應用通過 JNDI 在運作時實作與具體資源的動态綁定。Java EE 将系統的 cross-cutting concern下沉到應用伺服器來解決,讓開發者隻關注應用的業務邏輯,開發效率有了較好的提升;同時減輕應用對環境和中間件實作的依賴,比如可以在開發環境中用 ActiveMQ ,在生産環境中使用 IBM MQ 替換,而無需修改應用邏輯。

我對雲原生軟體架構的觀察與思考

在架構上,Java EE 是一個大的單體應用平台,拖慢了自身架構疊代的速度,跟不上時代的變化。由于Java EE過于複雜、沉重,在微服務興起之後已經被大多數開發者所遺忘。

在雲原生的時代,我們到底需要什麼樣的應用運作時?

Dapr(

https://dapr.io/)

是微軟給出的答案。Dapr 是一個事件驅動的,可移植的,建構微服務應用的運作時環境。支援應用在雲或邊緣部署,支援語言與架構的多樣性。Dapr利用 Sidecar 的模式,把應用邏輯中的一些橫切關注點需求(Cross-cutting)分離和抽象出來,進而達到應用與運作環境的解耦以及對外部依賴(包括服務之間)的解耦。

我對雲原生軟體架構的觀察與思考

Dapr 的功能和定位如上圖所示:

1、最底下基礎設施是各種雲平台或者邊緣環境。

2、其上是 Dapr 運作時和“building block” (構件)。Dapr 構件解耦了外部服務和服務的消費者,可以按需加載。構件以統一的 HTTP/gPRC API 為應用層提供服務通路。我們可以将外部服務從 Amazon DyanamoDB 切換為 Azure ComosDB ,上層應用無需修改任何代碼。Dapr 運作時作為一個獨立的 sidecar 程序,獨立于應用邏輯。

3、應用通過輕量化的 SDK 來簡化對構件 API 的調用,基于 gRPC/HTTP 開放協定可以輕松支援多語言。

盡管 Dapr 和 Service Mesh 在架構上有些類似,服務治理功能有所重疊,但兩者在本質上卻大有不同。服務網格對應用是透明的基礎設施;而 Dapr 為狀态管理,服務調用和故障處理,資源綁定,釋出/訂閱,分布式跟蹤等提供抽象,需要應用程式通過 SDK/HTTP/gRPC 顯式調用 Dapr 能力,它是面向開發人員的開發架構。

Dapr 還非常年輕,還在快速疊代中,距離被廣大開發者和三方廠商所支援還有很長的路要走。但是 Dapr 給我們揭示出一個新的方向:通過關注點分離,讓開發者隻需關注業務邏輯自身,而分布式架構的系統關注下沉到基礎設施中實作;讓業務邏輯與外部服務解耦,避免廠商綁定;同時應用和應用運作時是兩個獨立的程序,通過标準化API進行互動,生命周期解耦,便于更新和疊代。

Serverless 的機遇與挑戰

在上一篇文章中,我已經對 Serverless 應用基礎設施,如函數即服務(FaaS), Serverless 容器做了介紹。本文談談函數即服務 FaaS 應用在架構方面的一些思考。

FaaS 的核心思維是:開發者不必關心基礎設施運維、容量規劃或者擴容縮容,隻需為使用的雲資源和服務付費既可。這個思考的背後是:讓開發者避免投入基礎設施的運維,盡可能複用現有的雲服務能力,讓開發時間重新配置設定到對使用者有更有直接影響和價值的事情上,比如健壯的業務邏輯、能吸引使用者的界面及快速響應、可靠的 API 上。

在軟體架構層面中, FaaS 将複雜的業務邏輯拆解成一系列細粒度的函數,并通過事件驅動的方式觸發調用。函數之間是松耦合的,可以通過如下兩種典型的模式進行協同、組合。

Workflow Orchestration 工作流編排:以阿裡雲 Serverless 工作流為例,可以通過一個聲明式的業務流程來編排任務。這種方式簡化了開發和運作業務流程所需要的任務協調、狀态管理以及錯誤處理等繁瑣工作,讓開發者聚焦于業務邏輯開發。

我對雲原生軟體架構的觀察與思考

Event Choreography 事件協調:函數服務之間通過事件交換消息,由事件總線等消息中間件來進行事件的轉發,并觸發其他函數執行。下面是一個示例應用場景,通過 EventBridge,将訂單,使用者通知、商家通知、接單、結單等基于函數實作的業務邏輯串聯在一起。這種方式更加靈活,系統的健壯性也更好。但是缺點是缺乏顯式的模組化,開發和維護相對較複雜。

我對雲原生軟體架構的觀察與思考

Serverless 具備很多優勢, 比如:降低運維成本,提升系統安全性,提升研發效率,加速業務傳遞等等。然而 Serverless 還有一些不能回避的問題需要我們來做判斷:

成本管理: 對于“Pay as you go”的收費模式的一個弱點是無法準确預測具體會産生多少費用,這于許多組織預算管理的方式不同。

廠商鎖定: 即使 Serverless 應用基于開放的語言和架構,但是多數Serverless應用還依賴一些非标準化的 BaaS(Backend as a Service)服務,如對象儲存,Key- Value 資料庫,認證,日志,監控等。

調試和監控: 與傳統應用開發相比, Serverless 應用的調試與監控工具能力還不完善。良好的可觀測性是将 serverless 計算的重要助力。

架構複雜性:Serverless 開發者無需關注底層基礎設施的複雜性,但是應用架構的複雜性需要格外關注。事件驅動架構和細粒度函數微服務,與傳統開發模式非常不同。大家需要根據業務需求和自己的技術能力,在合适的場景應用,然後逐漸擴大應用範圍。

關于典型的 Serverless 應用架構,大家可以參考 What a typical 100% Serverless Architecture looks like in AWS ! :

https://medium.com/serverless-transformation/what-a-typical-100-serverless-architecture-looks-like-in-aws-40f252cd0ecb

Cloud Programming Simplified: A Berkeley View on Serverless Computing:

https://www2.eecs.berkeley.edu/Pubs/TechRpts/2019/EECS-2019-3.pdf

也是深入了解 Serverless 計算的一個好的參考。

應用運作時的靈活進化

更快、更輕、更靈活的應用運作時技術是雲原生計算的持續追求。

體積更小 - 對于微服務分布式架構而言,更小的體積意味着更少的下載下傳帶寬,更快的分發下載下傳速度。

啟動速度更快 - 對于傳統單體應用,啟動速度與運作效率相比不是一個關鍵的名額。原因是,這些應用重新開機和釋出頻率相對較低。然而對于需要快速疊代、水準擴充的微服務應用而言,更快的的啟動速度就意味着更高的傳遞效率,和更加快速的復原,以及更快的故障恢複速度。

占用資源更少 - 運作時更低的資源占用,意味着更高的部署密度和更低的計算成本。

正因為此,Golang、Node.js、Python 等語言開發者在持續攀升,有幾個值得大家關注的技術:

在 Java 領域,GraalVM (

https://www.graalvm.org/

)已經逐漸成熟。它是基于HotSpot上增強的一個跨語言的全棧虛拟機,支援衆多語言的運作平台(包括Java、Scala、Groovy、Kotlin、JavaScript、Ruby、Python、C、C++等)。GraalVM允許您将程式提前編譯為本地可執行檔案。

與經典Java VM相比,生成的程式具有更快的啟動時間和更低的運作時記憶體開銷。Quarkus(

https://quarkus.io/

)/Micronaut(

https://micronaut.io/

) 等作為雲原生定制的新一代Java架構,可以實作驚豔的啟動時間和資源開銷。更多分析可以參考Java的雲原生進化。

WebAssembly 則是另外一個令人激動的技術。WebAssembly 作為一個面向現代 CPU 體系架構設計的,安全的、可移植、高效率的虛拟機沙箱,可以在任何地方(伺服器、浏覽器、IoT等等)、任何平台(不同作業系統,不同CPU體系架構下)安全運作應用。WebAssembly System Interface(WASI)是來标準化 WebAssembly 應用與系統資源的互動抽象,比如檔案系統通路,記憶體管理,網絡連接配接等,提供類似 POSIX 這樣的标準 API 。

平台開發商可以針對具體的作業系統和運作環境提供 WASI 接口不同的實作,可以在不同裝置和作業系統上運作跨平台的 WebAssembly 應用。這可以讓應用執行與具體平台環境實作解耦,使得應用“Build Once, Run Anywhere”的理想逐漸形成現實。雖然目前 WebAssembly 已經超越了浏覽器的領域,但是其發展還在非常初期,期待社群共同推動。有興趣的同學可以看看 WebAssembly 與 Kubernetes 雙劍合璧:

https://www.infoq.cn/article/rEcOgQiurqaTyY7dJ6hA

趨勢總結

我對雲原生軟體架構的觀察與思考

雲原生軟體架構還在快速發展中,涉及的内容也非常廣泛。上述内容更多是個人總結、了解和判斷,期待與大家的交流和深入探讨。

更多參考:

https://martinfowler.com/architecture/
https://www.ibm.com/cloud/blog/7-missing-factors-from-12-factor-applications https://www.infoq.com/articles/microservices-design-ideals/ https://www.infoq.com/articles/architecture-trends-2020/ https://theburningmonk.com/2020/08/choreography-vs-orchestration-in-the-land-of-serverless/