開發與維運

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

image.png
作者 | 易立,阿里雲資深技術專家,容器技術負責人

本系列文章:
第一篇 - 雲原生基礎設施 (已發佈,文末點擊閱讀原文查看)
第二篇 - 雲原生軟件架構(本文)
第三篇 - 雲原生應用交付與運維體系(待續)

前言

在《雲原生基礎設施》一文中我們談到了,雲原生計算包含三個維度的內容,雲原生基礎設施,軟件架構和交付與運維體系,本文將聚焦於軟件架構層面。

“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 發佈十二要素應用宣言。它為構建一個優雅的互聯網應用,定義了需要遵循的一些基本原則和方法論,也廣泛影響了眾多的微服務應用架構。十二要素重點關注:應用程序的健康成長,開發者之間的有效的協作,以及避免軟件架構腐化的影響。其內容在今天也值得每個同學認真體會。

image.png
圖片來源: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 年提出的構想:在這種模式中,任何基礎設施的實例(包括服務器、容器等各種軟硬件)一旦創建之後便成為一種只讀狀態,不可對其進行任何更改。如果需要修改或升級某些實例,就是創建一批新的實例進行替換。這種模式的可以減少了配置管理工作的負擔,保障系統配置變更和升級可以可靠地重複執行,避免令人頭疼的配置漂移問題;易於解決部署環境間的差異,讓持續集成與持續部署過程變得更流暢;支持更好的版本管理,在部署出錯時可進行快速回滾,

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

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

自包含可觀測性

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

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

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

image.png
對於啟動緩慢的應用,為了避免在應用啟動完成之前將流量導入。Kubernetes 支持業務容器提供一個 readiness 探針,對於 HTTP 類型探針,Kubernetes 會時訪問該地址,如果該地址的返回碼不在 200 到 400 之間,則認為該容器無法對外提供服務,不會把請求調度到該容器;
image.png
同時在新的微服務架構中已經內置了可觀測探針,比如在 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

此外,應用可觀測性包含三個關鍵能力:日誌、監控與鏈路追蹤。
image.png
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/
image.png

其中,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)實現。隨著業務的快速發展,異構系統之間的集成逐漸成為挑戰。

image.png
https://philcalcado.com/2017/08/03/pattern_service_mesh.html

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

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

上圖是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 “服務網格概念浮出水面,一個概念示意圖如下:
image.png

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

新一代分佈式應用運行時

對於分佈式應用,Bilgin Ibryam 在Multi-Runtime Microservices Architecture :
https://www.infoq.com/articles/multi-runtime-microservice-architecture/,文中分析並總結了典型的四大類需求:

生命週期(Lifecycle)

網絡(Networking)
狀態(State)
捆綁(Binding)
image.png
熟悉傳統企業架構的同學可能發現,傳統的 Java EE (現在改名為 Jakarta EE )應用服務器的目標也是解決類似的問題。一個典型 Java EE 應用服務器的架構如下圖所示:應用生命週期由各種應用容器管理,如 Web 容器,EJB 容器等。應用的安全管理、事務管理、連接池管理都是交給應用服務器完成。應用可以通過 JDBC 、JMS 等標準 API 接口訪問外部的企業中間件,如數據庫、消息隊列等。

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

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

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

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

image.png

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 工作流為例,可以通過一個聲明式的業務流程來編排任務。這種方式簡化了開發和運行業務流程所需要的任務協調、狀態管理以及錯誤處理等繁瑣工作,讓開發者聚焦於業務邏輯開發。

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

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

趨勢總結

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

更多參考:

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/

Leave a Reply

Your email address will not be published. Required fields are marked *