閒魚基於Dart生態的FaaS前端一體化建設

背景

隨著Flutter對現有業務的不斷參透,閒魚Serverless基建的重心也傾向了dart生態,先是將dart容器打包到服務器上,實現dart編程語言的統一,在統一的容器之上實現編程框架一體化(nexus、story),以及後端領域服務一體化。基於dart生態下,前端的FaaS在研發交付其實並不高效,研發階段主要面臨的問題是:

編程語言不統一:編程語言本身雖然不是最大的障礙,但這也確實給前端開發者增加不少門檻,而且更重要的是語言背後的生態、環境與體系更是一道高高的牆。

工程割裂與背後環境複雜:端側一個工程,FaaS側也有一個獨立的工程,它們背後都有著自己的一套構建、調試、集成/發佈的工具鏈;除此之外FaaS還有自己配套的環境、runtime、框架作為支撐。開發者面對這樣複雜的FaaS研發環境與雙重的研發工作流是無法做到高效交付的。

編程語言一體化

Typescript作為Javascript的超集,彌補了Javascript的靜態類型檢查,同時擴展了很多OOP的語法特性,使得TS跟dart在語法特性上有非常多相似的地方,為後面的轉換提供了可能與便利。要實現語言層面轉換背後都會有一個小型的編譯器在支撐著,不過幸運的是Typescript官方已經提供語法解析器,通過它我們很容易就拿到一份可靠的AST,所以我們只需要實現一個dart generator就行了。生成器大致可以分為四個層面的工作:

  • 基礎語法轉換

  • 原生方法差異轉換

  • 業務框架橋接

  • 依賴庫與頭文件橋接

基礎語法轉換

這部分很好理解,就是最基本的語法層面轉換,用個最簡單的例子看下。

閒魚基於Dart生態的FaaS前端一體化建設

原生方法差異轉換

兩種語言在內置原生方法上也有很大區別,舉個例子:可以看到下面數組的實例方法在兩種語言體系上是不一致的,除了數組插入還有很多很多原生方法是不一致的。當然也沒太必要被這個難以想象的數量嚇到,大多數情況:90%的場景只會用到那10%的方法,完成了10%的轉換就能cover到90%的場景。

  1. <code>// ts/<code>

  2. <code>list2.push(10)/<code>


  3. <code>// dart/<code>

  4. <code>list2.add(10)/<code>

要實現系統方法的差異轉化首先要識別出該方法是來自於哪個類,比如說 <code>list2.push(10)/<code>我不可能只檢查<code>push/<code>,因為隨便一個類/對象都可以實現一個push方法。我們必須識別出<code>list2.push/<code>的<code>push/<code>屬於<code>Array.push/<code>,別忘了整個typescript編譯器中佔比最大的類型檢查器<code>ts.TypeChecker/<code>,它可以很好的幫我們解決這個問題。大致思路如下:

閒魚基於Dart生態的FaaS前端一體化建設

業務框架橋接

在完成上面兩塊能力轉換後,常規裸寫一段邏輯進行轉換問題是不大的;但業務是不可能裸寫,業務需要框架,需要藉助框架進行通訊、與容器打交道。需要藉助框架進行業務抽象,更好的組織、管理業務邏輯。我們來看個例子:

  1. <code>DartMtopResult result = awaitHsfServices.request(moduleName, parameter);/<code>

上面的這段代碼是用於在dart側進行內部服務請求的,從代碼表明我們可以獲取到三部分信息:

  1. 有一個HsfServices的類

  2. HsfServices有一個同步返回結果的request方法,接收兩個參數

  3. 最終返回DartMtopResult的數據結構

我們再翻一下 <code>request/<code>的實現與<code>DartMtopResult/<code>的申明:

  1. <code>// DartMtopResult.dart/<code>

  2. <code>classDartMtopResult implements xxxx {/<code>

  3. <code>T data;/<code>

  4. <code>bool success;/<code>

  5. <code>String errMsg;/<code>

  6. <code>String errCode;/<code>


  7. <code>// more code hidden/<code>

  8. <code>}/<code>


  9. <code>// HsfServices.dart/<code>

  10. <code>classHsfServices{/<code>

  11. <code>// more code hidden/<code>

  12. <code>staticFuture> request(String moduletName, String parameter) async{/<code>

  13. <code>// more code hidden/<code>

  14. <code>}/<code>

  15. <code>// more code hidden/<code>

  16. <code>}/<code>

就看這麼多足夠了,打個比方如果我希望在typescript側編寫一個能用 <code>HsfServices.request/<code>發請求的ts代碼且不報錯,那應該怎麼做呢?像下面這樣申明一個:

  1. <code>// HsfServices.d.ts/<code>

  2. <code>export declare classHsfServices{/<code>

  3. <code>static request(moduletName: string, parameter: string): Promise>;/<code>

  4. <code>}/<code>

  5. <code>// DartMtopResult.d.ts/<code>

  6. <code>export declare classDartMtopResult {/<code>

  7. <code>data: T;/<code>

  8. <code>success: boolean;/<code>

  9. <code>errMsg: string;/<code>

  10. <code>errCode: string;/<code>

  11. <code>}/<code>


  12. <code>// business.ts/<code>

  13. <code>import{HsfServices} from"HsfServices.d.ts"/<code>

  14. <code>import{DartMtopResult} from"DartMtopResult.d.ts"/<code>

  15. <code>const result: DartMtopResult= awaitHsfServices.request>('recycleGet', parameter);/<code>

非常簡單就能讓業務邏輯正常寫下去並且不報錯。但你肯定會說這樣的代碼也沒法運行起來,是的,但我並不需要上面代碼運行起來,我需要的是將它轉成dart,並能在dart runtime中運行就可以了。大致的橋接思路如下:

閒魚基於Dart生態的FaaS前端一體化建設

依賴庫與頭文件橋接

這部分工作是從業務框架橋接中衍生出來的,我們還是用一個例子來說明一下問題產生的原因。

ts源碼如下:

  1. <code>// business.ts/<code>

  2. <code>import {HsfServices} from "@ali/faas-hsf"/<code>

  3. <code>import {DartMtopResult} from "@ali/faas-mtop-result"/<code>

  4. <code>const result: DartMtopResult = await HsfServices.request>('recycleGet', parameter);/<code>

dart源碼如下:

  1. <code>// business.dart/<code>

  2. <code>import 'package:hsf_services/hsf_services.dart';/<code>

  3. <code>import 'package:dart_mtop_result/dart_mtop_result.dart';/<code>

  4. <code>DartMtopResult result = await HsfServices.request(moduleName, parameter);/<code>

可以看到上面邏輯除了發請求部分要轉成dart,還有業務引用頭文件需要橋接過去,而頭文件的引入通常是靠pub依賴包(pubspec.yaml)安裝進來的,就意味著轉換器需要拿到 <code>@ali/faas-hsf/<code>對應dart側的pub包與引入頭文件。我們的解決思路大致是這樣的:在<code>@ali/faas-hsf/<code>模塊中放入<code>faas.yaml/<code>文件來指定對應的映射關係。

  1. <code>@ali/faas-hsf/<code>

  2. <code>|--lib//<code>

  3. <code>|--faas.yaml/<code>

  4. <code>|--package.json/<code>


  5. <code>// faas.yaml/<code>

  6. <code>faas_pub:/<code>

  7. <code># 映射的dart側依賴包/<code>

  8. <code>hsf_services: ^1.1.7/<code>

  9. <code># 映射引入頭文件/<code>

  10. <code>index: hsf_services.dart/<code>

研發過程中再通過工程腳手架來自動完成這之間的映射關係的提取:頭文件映射與依賴包映射。頭文件映射最終會交給轉換器,而依賴包映射會交給背後自動維護著的dart工程(後面會提到背後自動維護的dart工程)。大概的思路如下圖所示:

閒魚基於Dart生態的FaaS前端一體化建設

研發工程一體化編程語言一體化只是整個FaaS一體化研發的第一步,也只有統一了編程語言之後,背後的生態(npm)、工具鏈(build)與工程才可能一體化。我們看下現狀:開發者面對的是兩個割裂的工程,兩套不同的環境、生態。這正如文章一開始所說的:編程語言本身不是最大的障礙,但語言背後的環境與生態卻是一道高高的牆。在我們統一編程語言之後,研發工程一體化就變得可行了。

閒魚基於Dart生態的FaaS前端一體化建設

正如上圖所示,FaaS工程本身的複雜在於整個工程需要運行在一個本地容器之中,因為容器要為工程提供runtime、相應的工具鏈、框架依賴等能力。所以本地容器本身是必不可少的,我們能做的只是儘可能讓開發者無感容器的存在;除此之外還要對兩個工程的邏輯做一定的融合,大致可以抽象成四部分工作:研發代碼層面融合代碼層面融合包括兩部分:業務邏輯融合、業務邏輯所依賴編程框架融合。分別體現在<code>faas_src/<code>存放業務邏輯的ts版,<code>package.json/<code>存放業務邏輯所依賴的編程框架(前面我們介紹到業務框架橋接最終就體現在端側的依賴包上)

  1. <code>├── faas_pub.yaml/<code>

  2. <code>├── faas_src/<code>

  3. <code>│ └── Home/<code>

  4. <code>│ └── index.ts/<code>

  5. <code>├── package.json/<code>

  6. <code>├── src/<code>

  7. <code>│ ├── components/<code>

  8. <code>│ └── pages/<code>

  9. <code>│ └── Home/<code>

  10. <code>│ ├── index.css/<code>

  11. <code>│ └── index.js/<code>

  12. <code>└── README.md/<code>

FaaS側的工程黑盒化使端側腳手架全權接管FaaS側的工程初始化、熱部署、調試信息,暴露出來給開發者的只有一套工具鏈,只有兩個指令<code>init/<code><code>dev/<code>,讓開發者0門檻初始化出一套統一而可靠環境的FaaS工程。

閒魚基於Dart生態的FaaS前端一體化建設

對接編譯器進行研發實時編譯

這部分主要負責對接轉換器實時將ts編譯成dart,並同步到黑盒中的FaaS工程。在實時編譯過程有兩部分內容:一部分是純ts邏輯編譯成dart,另一部分是依賴包的同步安裝,其中 <code>faas_pub.yaml/<code>由腳手架通過探測端側package.json中的faas依賴包來進行提取生成的,並不需要人工維護。

閒魚基於Dart生態的FaaS前端一體化建設

串聯調試階段的編譯流從保存每一個改動到在瀏覽器上能成功發起一個faas函數請求,這之間大致經過這些步驟:監控改動、編譯代碼、產物部署的流,由統一的端側腳手架進行串聯起來。

閒魚基於Dart生態的FaaS前端一體化建設

效果

經過編程語言的一體化後,我們不僅為開發者提供一種熟悉的技術棧,也為後面工程一體化提供了可能性;再經過工程一體化後,我們為開發者解決了工程割裂,解決背後複雜的FaaS本地運行環境,帶來與原研發模式基本一致的研發體驗。

閒魚基於Dart生態的FaaS前端一體化建設

後續

一體化之路還很多要去建設的,調試、發佈、回滾等等;除此之外,FaaS畢竟還是運行在後端,最終通過網絡協議與端側通訊,那在兩份代碼中必然存在兩份數據結構申明,兩套封包解包邏輯;這為後面數據結構的一體化與自動化建設提供了很好的發揮餘地。

閒魚技術團隊不僅是阿里巴巴集團旗下閒置交易社區的創造者,更是移動與高併發大數據應用新技術的引導者與創新者。我們與Google Flutter/Dart小組密切合作,為社區貢獻了多個高star的項目和大量PR。我們正在積極探索深度學習和視覺技術在互動、交易、社區場景的創新應用。閒魚技術與集團中間件團隊共同打造的FaaS平臺每天支持數以千萬級用戶的高併發訪問場景。

就是現在!客戶端/服務端java/架構/前端/質量工程師面向社會+校園招聘,base杭州阿里巴巴西溪園區,一起做有創想空間的社區產品、做深度頂級的開源項目,一起拓展技術邊界成就極致!

*投餵簡歷給小閒魚→

[email protected]

閒魚基於Dart生態的FaaS前端一體化建設

開源項目、峰會直擊、關鍵洞察、深度解讀

請認準閒魚技術


分享到:


相關文章: