腳踏實地“敲代碼”,同時,不忘偶爾仰望下星空。

本文根據阿里云原生團隊資深技術專家的直播整理而成,從領域驅動設計(DDD)、Reactive、Service Mesh 和代碼智能等幾個方面展開,聊一聊他眼里的軟件開發的發展趨勢。
今年 4 月份的時候,InfoQ 發布了軟件架構與設計的趨勢報告。InfoQ 在技術趨勢報告中將軟件架構分為了 4 類,如下圖所示,從左到右依次是創新者(Innovators)、早期采用者(Early Adopters)、早期大眾(Early Majority)和晚期大眾(Late Majority)。在報告中可以看出,很多技術如微服務(Microservice)、領域驅動設計(Domain-driven Design)等已經非常流行,并成為如今軟件開發行業的主流了。對于早期大眾和晚期大眾部分的架構設計而言,很多部分與領域驅動設計相關,比如微服務、CQRS、事件驅動等。

在本次的分享中,也會介紹為什么領域驅動設計相關的技術會在InfoQ技術報告中占據如此重要的位置。并且也將會根據領域驅動設計展開到其他相關技術,比如 Service Mesh 等。
領域驅動設計(Domain-drivenDesign)
GitHub 上有一個 DDD 實踐指南,從下圖中可以看到領域驅動設計所解決的問題從架構設計一直到代碼層級,所以對于整個軟件研發周期來說,DDD 方法論還是比較流行的。而指導微服務劃分的一個重要理論基礎就是領域驅動設計。之前談到領域驅動設計,大部分時間總會探討如何識別限界上下文,如何使用戰術或者戰略方法等,而本次分享中則會介紹一個主流的趨勢,那就是 DDD + Reactive 。

之前的時候,領域驅動設計有分層結構、事件驅動等概念,但是也存在一個很大的問題就是將單體應用劃分成多個應用的時候,如何解決應用之間的通信問題。領域驅動設計中有限界上下文映射(BoundedContext Map)的概念,來解決兩個限界上下文之間的通信。之前 DDD 談到的解耦合方式主要是基于消息實現異步化的理論支撐,但是具體如何去做卻沒有具體的技術棧幫助大家實施。當然了,針對于每一種語言而言,具體實現可能不同,因為一些語言是純異步化支持的,可能已經內置了協程支持,因此在不同的技術棧里面會選擇不同的解決方案。本次分享中比較偏向于 Java 的技術棧,但是需要強調的是 Reactive 并不止針對 Java 。

Reactive 的好處在于使得兩個限界上下文之間的通信變成一個技術棧,這樣就可以使用Reactive 實施了。JDDD 是一個新的項目,其基于 Spring Data 實現。JDDD 也是一個開發包,其主要思想是讓開發者通過開發包以代碼的方式來呈現 DDD 要表達的思想。前面所提到的微服務則非常依賴限界上下文,借助它來確定邊界并進行劃分。此外,在 Could Native 和 FaaS 方案都會提到是面向事件驅動的,但是事件(Event)這一概念在領域驅動設計中很早的時候就已經提出來了,如果涉及到事件或者異步化相關的事情,就需要關注事件和領域驅動設計的關系了,當然目前來說,這一點已經非常成熟了。還有一點就是 DDD + CQRS , CQRS 簡單理解就是讀寫分離,比如 MySQL 數據庫使用 Master-Slave 機制使得讀數據和寫數據分開, Master 主要負責寫入, Slave 主要負責讀取,而 CQRS 也會在 DDD 中發揮很大的作用。目前來看,在整個架構設計里面,領域驅動設計是非常主流的,能夠幫助我們解決一些設計上面的問題。
Reactive
DDD將應用劃分成多個小的限界上下文,接下來需要解決如何通信的問題了。而Reactive就提供了不同應用之間進行通信的指導方案,當然了Reactive也提供了對應的技術解決方案,比如Async能夠提供全異步化支持,當然這里的異步化只是說基于異步化技術。此外,還有Observable模式,可以說這是一個架構和設計的模式,這一模式應用很廣泛,最早是由微軟提出的,比如一個表格發生變化,其他表格也會根據這個表格發生相應的變化,這就是Observable觀察者模式。針對Java程序員而言,可以發現Spring在主推SpringReactive,很多技術方案都是和Reactive相關的。此外,還有Message和EventDriven,FaaS和CouldNative在做通訊的時候都需要根據Message和EventDriven進行設計。

RSocket
在InfoQ的軟件架構和設計趨勢圖中,在早期采用者階段有一個FunctionProgramming,在早期大眾階段則有一個ReactiveProgramming,而在創新者部分則有一個RSocket&ReactiveStreams,大家對于這個技術可能了解比較少,因此在本部分簡單介紹一下。如果大家關注Spring官方站點,那么可能了解Spring5.2版本已經內置了RSocket的支持,但是并沒有很多相關文檔來介紹相關技術。前面提到,Reactive是讓DDD的通訊模型有了理論基礎,所以這個RSocket協議就是為了實現Reactive理論基礎設計一個通訊模型。
如果大家了解Reactive語義,就會知道有一個背壓模式。當然,背壓模式與斷路保護模式類似,兩者的機制都是為了解決限流問題,因為消費方或者服務提供方因為突發流量不能支撐導致應用雪崩或者宕機。傳統模式最早使用消息隊列,是消息提供方主動將消息推送給消息消費方,此時存在的問題就是消費方消費不了,但是消息提供方還會繼續推送消息,使得消息消費方無法支撐導致系統不穩定。
后來出現了Kafka,其使用Pull模式,也就是需要多少條消息就去拉取多少條消息,因為Kafka支持消息堆積。但是使用Pull模式則需要設置時間間隔,此時出現的問題就是消息輪空怎么辦?也就是當發送消息時發現消息沒有就會輪空,此時就會不停地向Broker拉數據,這樣就會造成網絡浪費。所以在Reactive里面的背壓是將原來的推送模型增加了開關,當消息到達一定數量之后就不要推送了。這種主動推送機制的好處在于性能特別高,不需要去拉取,也不需要保存消息位點。總之,背壓機制能夠設置所能夠支持的最大拉取數量,如果在閾值的范圍內能夠做到性能的增高,同時也能保護消息的消費方。

在通訊方面,RSocket有四個模型,比如Request和Response、發布-訂閱模型、日志采集等。在使用DDD實現應用劃分之后,兩個應用之間需要進行通信,而RSocket就提供了四個模型,基本涵蓋了應用之間通信的場景,此外還提供了MetadataPush,比如所有的集群變化或者需要做限流都可以通過MetadataPush來實現。
RSocket還可以做對等通訊,傳統的通訊模型都是Client-Server模式,但是在RSocket下通信都是對等的,沒有Client和Server的分別,相互之間都可以進行消息發送。前面所提到的RSocket的設計模型和理念都是為了解決基于Reactive通信應用的各種問題。雖然RSocket這個技術方案并沒有得到很好的推廣,但是已經有很多技術社區提供支持了,比如Spring在5.2版本中就增加了對于RSocket的支持。因為推出的通信協議是Reactive的,但是背后需要涉及到數據庫操作或者其他等如果不是異步化的,就需要進行處理,Spring在今年發布SpringBoot2.3的時候就完善了對于Reactive的支持。舉例而言,從最前端的網關層通過Spring的WebFlask就能夠保證是Reactive的。
中間件、通訊模型以及NoSQL產品都是支持異步化操作的,能夠解決高并發問題,這些特性在很早的時候就已經添加到Spring里面了。此外,在開發的時候還有一點比較關鍵的就是數據庫,每個Java程序員在訪問數據庫的時候首先要設置數據庫連接池,需要考慮數據庫設計方案以及考慮哪一個連接池性能最高等問題。這是因為數據庫不屬于異步化的,需要通過創建多個連接來解決并發的問題。因此,在Spring的R2DBC里面增加了對于數據庫異步化的支持。目前Spring能夠支持數據庫的異步化了,但是還需要一段時間的等待,因為R2DBC和JDBC一樣,也是一個規范+驅動,但是基于JDBC的上層框架非常多,比如Hibernate、MyBatis等,而基于R2DBC的框架并不多,目前只有一個SpringDataR2DBC。因此,國內很多的開發者首選還是MyBatis。如果存在實現異步化的意向,那么基本上是從最開始接入的網關層協議一直到NoSQL數據庫,包括存儲文件系統,全部都實現異步化,這些目前也比較成熟了。
ServiceMesh
再回到最開始InfoQ的軟件架構與設計趨勢,可以看到大多數與CloudNative相關,比如Serverless、ServiceMesh、HTTP/2和gRPC等,這些概念非常火爆,因此可以說ServiceMesh在目前的技術趨勢中占據很高的地位。典型的ServiceMesh架構還是基于Istio+Envoy的Sidecar經典架構。后來出現了Dapr,它與Isito+Envoy架構的區別在于Envoy的Sidecar可以理解為代理,其目的在于將服務連接起來,Dapr也會有一個Sidecar,這個Sidecar不只是做代理那么簡單,而是可以幫助開發者做非常多的事情,比如使用新的開發語言實現應用,但是Kafka或者NoSQL數據庫并沒有提供對應語言的SDK,或者即便提供了對應語言的SDK,但是這些SDK并不穩定,此時選擇一些新的技術就會受到一些約束。舉個例子,如果大家做一些大數據相關的工作,比如HBase、Hadoop等都是Java相關的SDK最穩定。而Dapr的Runtime可以很好地與外部系統交互,比如應用gRPC與Dapr的Sidecar通信,也可以和Kafka通信,將從Kafka中獲取的數據傳遞給gRPC,并且Dapr的Runtime使得開發者不需要了解通信協議背后的細節。

這樣的設計比Isito+Envoy架構要更好,因為如果只做代理,那么在客戶端還需要做大量的數據處理工作,而如果放到Proxy上來實現,就會更加復雜,此時就會變成Dapr的Runtime。此外,阿里巴巴最近在做一些嘗試就是RSocketBroker,其基于ReactiveMesh實現,其與Sidecar結構不同,因為大多數基于基于消息或者事件驅動的都只需要發送消息即可,而RSocketBroker提供了應用之間的一攬子通信協議,不需要再選擇其他通信協議了。對于以上三種技術方案應該如何選擇,大家應該根據自身的實際情況進行判斷,因為技術選型沒有絕對的正確,但是目前來說是Isito+Envoy架構收到了大多數人的歡迎,因為這與Kurbernates整合比較方便,并且目前已經提供了比較完善的基礎設施。
FaaS
對于企業而言,可能核心系統只有幾個,但是總會有一些長尾的應用。如果之前沒有很好的FaaS方案,那么可能會做一個大而全的系統將這些小功能融合在一起,此時造成的問題就是修改代碼的困難,造成很多的麻煩,也不利于技術革新。FaaS的好處在于可以將函數部署到邊緣,比如CDN邊緣或者Edge端。在通信部分,FaaS主要是Message+EventDriven的,而目前很多的通信協議會用到gRPC,因為FaaS本身要求響應速度比較快,因此對于通信協議具有一定的要求。gRPC的接口相對比較底層一些,在此之上還做成了ReactivegRPC能夠更方便大家通過Reactive來操縱gRPC,但是在寫代碼的時候可能并不知道是底層是基于gRPC的。在技術趨勢里面還有一個叫做AsyncAPI,其實在大家工作中,很多框架都支持從代碼直接生成OpenAPI,比如SpringDoc等。

目前還有一個技術趨勢是FaaSWebAssembly,其也是和FaaS結合的。眾所周知,如果FaaS不是高頻使用的話,會出現容器拉起時出現一定等待時間的問題,但是在某些場景下,對于等待時間存在比較嚴格的限制。那么此時如果使用傳統容器的解決方案往往難以實現,而WebAssembly這種技術解決方案就能夠充當函數的鉤子,能夠滿足上述需求。同時,由于WebAssembly也是W3C新推出的規范,和之前的HTML、CSS、JS網頁三劍客結合成為了網頁四劍客。此外,在FaaS部分,還有最近推出的Deno可以很好地支持WebAssembly機制。而因為很多開發同學會關心Rust這一編程語言,而Deno對于Rust具有良好的支持,因此很多人會選擇使用Deno作為FaaS的Runtime來支持FaaS的運行。
代碼智能
目前還有一個技術趨勢就是代碼智能,通過前面所提到的技術架構,能夠滿足企業的架構選型,那么接下來就肯定會涉及到代碼。對于代碼而言,主要有兩點,一個是代碼生成,另一點是手寫代碼。代碼生成主要是腳手架,能夠快速將代碼的框架生成,而不需要自己寫配置項等,目前這項技術已經非常成熟了。這部分的工具主要有IntelliCode、Codota、Kite以及各種CloudIDE等。在寫代碼的時候會遇到的問題就是技術往往變化很快,每間隔4、5個月就會推出新的框架或者技術,而因為開發者關注的是開發效率,所以即使有CodeAI能力,但是在某種程度上還是會依賴CloudIDE特性。因為CloudIDE在跟進某些技術上面的速度比較快,它往往能夠很快知道客戶新的需求是什么。

CodeGeneration+IDE
目前有很多的代碼生成工具,幫助大家解決框架生成問題。比如Scaffold,start.spring.io是可擴展的代碼框架生成器。CodeGeneration是Java自帶的Annotation機制,這一點可能大多數同學沒有關注,但是在很多的場景下也會使用到,其生成的代碼能夠做到性能更高。此外,目前很多的IDE,比如JetBrains+VSCode都有集成的工具,幫助開發者快速生成代碼框架結構。

6月12-13日,阿里巴巴研發效能峰會,由雷卷出品的架構設計與代碼智能專場,將會有更多DDD、Reactive、ServiceMesh、代碼智能相關的精彩分享。
不僅如此,本次峰會是阿里內部研發效能峰會首度對外,7大論壇、35個議題、39位阿里內外部的技術大咖、1300分鐘的干貨分享,將覆蓋云原生、架構設計、代碼智能、數字化轉型等眾多熱門技術話題。