天天看點

從架構到代碼:軟體開發最新趨勢解析

本文内容根據演講視訊以及PPT整理而成。

今年4月份的時候,InfoQ釋出了軟體架構與設計的趨勢報告。InfoQ在技術趨勢報告中将軟體架構分為了4類,如下圖所示,從左到右依次是創新者(Innovators)、早期采用者(Early Adopters)、早期大衆(Early Majority)和晚期大衆(Late Majority)。在報告中可以看出,很多技術如微服務(Microservice)、領域驅動設計(Domain-driven Design)等已經非常流行,并成為如今軟體開發行業的主流了。對于早期大衆和晚期大衆部分的架構設計而言,很多部分與領域驅動設計相關,比如微服務、CQRS、事件溯源等。

從架構到代碼:軟體開發最新趨勢解析

在本次的分享中,也會介紹為什麼領域驅動設計相關的技術會在InfoQ技術報告中占據如此重要的位置。并且也将會根據領域驅動設計展開到其他相關技術,比如Service Mesh等。

領域驅動設計(Domain-driven Design)

GitHub上也有一個技術趨勢報告,從下圖中可以看到領域驅動設計所解決的問題從架構設計一直到代碼層級,是以對于整個軟體研發周期來說,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在主推Spring Reactive,很多技術方案都是和Reactive相關的。此外,還有Message和Event Driven,FaaS和Could Native在做通訊的時候都需要根據Message和Event Driven進行設計。

從架構到代碼:軟體開發最新趨勢解析

RSocket

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

從架構到代碼:軟體開發最新趨勢解析

在通訊方面,RSocket有四個模型,如果做RestAPI就會知道,HTTP 1.1具有幾種通用的模型,比如Request和Response、釋出-訂閱模型、日志采集等。在使用DDD實作應用劃分之後,兩個應用之間需要進行通信,而RSocket就提供了四個模型,基本涵蓋了應用之間通信的場景,此外還提供了Metadata Push,比如所有的叢集變化或者需要做限流都可以通過Metadata Push來實作。此外,RSocket還可以做對等通訊,傳統的通訊模型都是Master-Slave的這樣的Client-Server模式,但是在RSocket下通信都是對等的,沒有Client和Server的分别,互相之間都可以進行消息發送。前面所提到的RSocket的設計模型和理念都是為了解決基于Reactive通信應用的各種問題。雖然RSocket這個技術方案并沒有得到很好的推廣,但是已經有很多技術社群提供支援了,比如Spring在5.2版本中就增加了對于RSocket的支援。因為推出的通信協定是Reactive的,但是背後需要涉及到資料庫操作或者其他等如果不是異步化的,就需要進行處理,Spring在今年釋出Spring Boot 2.3的時候就完善了對于Reactive的支援。舉例而言,從最前端的網關層通過Spring的Web Flask就能夠保證是Reactive的。中間件、通訊模型以及NoSQL産品都是支援異步化操作的,能夠解決高并發問題,這些特性在很早的時候就已經添加到Spring裡面了。此外,在開發的時候還有一點比較關鍵的就是資料庫,每個Java程式員在通路資料庫的時候首先要設定資料庫連接配接池,需要考慮資料庫設計方案以及考慮哪一個連接配接池性能最高等問題。這是因為資料庫不屬于異步化的,需要通過建立多個連接配接來解決并發的問題。是以,在Spring的R2DBC裡面增加了對于資料庫異步化的支援。目前Spring能夠支援資料庫的異步化了,但是還需要一段時間的等待,因為R2DBC和JDBC一樣,也是一個規範+驅動,但是基于JDBC的上層架構非常多,比如Hibernate、MyBatis等,而基于R2DBC的架構并不多,目前隻有一個Spring Data R2DBC。是以,國内很多的開發者首選還是MyBatis。如果存在實作異步化的意向,那麼基本上是從最開始接入的網關層協定一直到NoSQL資料庫,包括存儲檔案系統,全部都實作異步化,這些目前也比較成熟了。

Service Mesh

再回到最開始InfoQ的軟體架構與設計趨勢,可以看到大多數與Cloud Native相關,比如Serverless、Service Mesh、HTTP/2和gRPC等,這些概念非常火爆,是以可以說Service Mesh在目前的技術趨勢中占據很高的地位。典型的Service Mesh架構還是基于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。此外,阿裡巴巴最近在做一些嘗試就是RSocket Broker,其基于Reactive Mesh實作,其與Sidecar結構不同,因為大多數基于基于消息或者事件驅動的都隻需要發送消息即可,而RSocket Broker提供了應用之間的一攬子通信協定,不需要再選擇其他通信協定了。對于以上三種技術方案應該如何選擇,大家應該根據自身的實際情況進行判斷,因為技術選型沒有絕對的正确,但是目前來說是Isito+Envoy架構收到了大多數人的歡迎,因為這與Kurbernates整合比較友善,并且目前已經提供了比較完善的基礎設施。

FaaS

對于企業而言,可能核心系統隻有幾個,但是總會有一些長尾的應用。如果之前沒有很好的FaaS方案,那麼可能會做一個大而全的系統将這些小功能融合在一起,此時造成的問題就是修改代碼的困難,造成很多的麻煩,也不利于技術革新。FaaS的好處在于可以将函數部署到邊緣,比如CDN邊緣或者Edge端。在通信部分,FaaS主要是Message+Event Driven的,而目前很多的通信協定會用到gRPC,因為FaaS本身要求響應速度比較快,是以對于通信協定具有一定的要求。gRPC的接口相對比較底層一些,在此之上還做成了Reactive gRPC能夠更友善大家通過Reactive來操縱gRPC,但是在寫代碼的時候可能并不知道是底層是基于gRPC的。在技術趨勢裡面還有一個叫做AsyncAPI,其實在大家工作中,很多架構都支援從代碼直接生成OpenAPI,比如Spring Doc等。

從架構到代碼:軟體開發最新趨勢解析

目前還有一個技術趨勢是FaaS WebAssembly,其也是和FaaS結合的。衆所周知,如果FaaS不是高頻使用的話,會出現容器拉起時出現一定等待時間的問題,但是在某些場景下,對于等待時間存在比較嚴格的限制。那麼此時如果使用傳統容器的解決方案往往難以實作,而WebAssembly這種技術解決方案就能夠充當函數的鈎子,能夠滿足上述需求。同時,由于WebAssembly也是W3C新推出的規範,和之前的HTML、CSS、JS網頁三劍客結合成為了網頁四劍客。此外,在FaaS部分,還有最近推出的Deno可以很好地支援WebAssembly機制。而因為很多開發同學會關心Rust這一程式設計語言,而Deno對于Rust具有良好的支援,是以很多人會選擇使用Deno作為FaaS的Runtime來支援FaaS的運作。

代碼智能

目前還有一個技術趨勢就是代碼智能,通過前面所提到的技術架構,能夠滿足企業的架構選型,那麼接下來就肯定會涉及到代碼。對于代碼而言,主要有兩點,一個是代碼生成,另一點是手寫代碼。代碼生成主要是腳手架,能夠快速将代碼的架構生成,而不需要自己寫配置項等,目前這項技術已經非常成熟了。這部分的工具主要有IntelliCode、Codota、Kite以及各種Cloud IDE等。在寫代碼的時候會遇到的問題就是技術往往變化很快,每間隔4、5個月就會推出新的架構或者技術,而因為開發者關注的是開發效率,是以即使有Code AI能力,但是在某種程度上還是會依賴Cloud IDE特性。因為Cloud IDE在跟進某些技術上面的速度比較快,它往往能夠很快知道客戶新的需求是什麼。

從架構到代碼:軟體開發最新趨勢解析

Code Generation + IDE

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

從架構到代碼:軟體開發最新趨勢解析

更多的精彩内容,請關注2020阿裡巴巴研發效能峰會-架構設計與代碼智能專場。

從架構到代碼:軟體開發最新趨勢解析