閒魚 Flutter 架構演進和實踐 | Flutter 沙龍回顧

11 月 23 日,字節跳動技術沙龍 | Flutter 技術專場 在北京後山藝術空間圓滿結束。咱們邀請到字節跳動移動平臺部 Flutter 架構師袁輝輝,Google Flutter 團隊工程師 Justin McCandless,字節跳動移動平臺部 Flutter 資深工程師李夢雲,阿里巴巴高級技術專家王樹彬和你們進行分享交流。前端

如下是阿里巴巴高級技術專家王樹彬的分享主題沉澱,《閒魚 Flutter 架構演進和實踐》。算法

演講內容大綱:數據庫

  1. 閒魚爲何選擇 Flutter
  2. 閒魚的架構及其演進
  3. 回顧及展望

今天主要分享的內容是閒魚一年半在 Flutter 的演進,但願有某個階段某個場景和你們契合,給你們一些啓發和討論。編程

我的介紹

先作一個自我介紹,我是閒魚的架構負責人,在 2009 年就加入阿里了,2009 年是淘寶首次實現營收平衡的那一年,也是第一次雙十一的那一年,在那之後迎來了淘寶快速發展的時期,在那個快速發展的背景下,我作了商家百億級數據的在線分析系統以及營銷系統,再後來就是用到無線,在無線時代時去作手淘上 0 到 1 的地理圍欄和 LBS 的無線特性的項目。redux

閒魚是 2014 年創立的,2015 年我來到閒魚,經歷閒魚從早期到如今超過 2000 萬 DAU 的發展階段,這 4 年時間看到閒魚這樣漲起來。中間到了必定團隊規模之後,我就去負責閒魚架構,以適應閒魚的快速發展。小程序

閒魚爲何選擇 Flutter?

Flutter 也並非最完美的一種框架,沒有最好的只有最適合的,究竟是什麼場景讓閒魚選擇了 Flutter?架構

閒魚背後是高速發展的 C2C 市場

大的業務背景是這樣的。首先,閒魚是 C2C 的交易市場,隨着中國網購商品不斷豐富,社會上存在愈來愈多大量的閒置物品,出現了這樣一個存量市場,這個市場規模有多大?從閒魚角度看,在 2019 年財年,就有 6000 萬以上的用戶發佈了本身閒置的商品,這個數字還在增加,到如今天天已經有 100 萬人發佈超過 200 萬件商品。而後,由於它是 C2C 對等交易,這樣的用戶會造成更多互動和社區屬性,能夠看到閒魚裏已經沉澱 130 多萬個魚塘,造成這樣的社區。這是一個大的背景,就是高速發展的存量市場。app

閒魚客戶端側的業務特色

端側業務特色是什麼樣的呢?有幾個特色:框架

  1. 業務心智。閒魚是獨立的業務心智,促使咱們把主要開發力量投在閒魚 APP 上,有一些外投的場景,像投到手淘、支付寶或者外面的場景,那是少許的頁面。less

  2. 高蘋果用戶佔比。閒魚的特色是蘋果用戶佔比高。這個特色致使早期時咱們在設計上對設計的要求很是極致,兩端的設計要求一致且要以蘋果設計爲主,這時候若是作安卓開發的同窗就能夠想象到安卓這邊的痛苦了。

  3. C2C、社區業務背景。致使有不少互動、商品圖片及視頻的需求,另外快速發展期有不少大的業務嘗試。

閒魚的端技術選擇

這樣幾個特色就致使咱們下大決心遷到用 Flutter 來作,那是到了 2017 年末的時候。在中間也曾經嘗試過用 Weex。

如今閒魚的技術棧是這樣的:活動類和外投類的產品頁面用 H五、小程序、Weex 開發;主鏈路、產品化的頁面都用 Flutter 開發,這部分在閒魚的覆蓋率有 80%以上。原來 Native 開發同窗都轉型到用 Flutter 開發了。這些是技術因素,此外還有團隊也是很重要的因素,Native 同窗寫 Flutter 會轉換更快一些,閒魚有 Native 人員基礎,因此纔有決心深刻去作 Flutter。

閒魚的架構及其演進

這是發展到如今閒魚的架構:下面是從項目管理、打包平臺、持續集成到自動化測試;中間的包括頁面組件、Native 混合、音視頻組件等;再往上一點是承載業務的容器,讓業務可以把本身業務流程下沉的容器(Dynamic 和 Serverless)。

閒魚架構的演進

這個架構怎麼演進來的?大概有三個階段:第一,混合開發,引入期。第二,規模化,發展期,愈來愈多頁面和人員投入時,會面對一些新的問題。第三,一體化,新探索,成熟之後考慮下一步怎麼探索,原來只是從跨兩端角度提升效率,如今把雲(Server)也考慮進去,從雲端一體的角度考慮效能的進一步提高空間。

1、混合開發、引入期

引入期一般作些什麼事情?閒魚從最開始要用 Flutter,到最後上線用了 3-4 個月時間,由於的前車可鑑少。可是到今年,杭州有一家初創公司,它從決定開始用 Flutter,到最後上線,只用了大概 1 個月,能夠看出如今這個引入期成本已經降下來了。雖然降了,可是引入期最大的工做內容仍是在混合開發這一塊,也就是怎麼把 Flutter 混合到本身已有 APP,除非你是開發一個全新的純 Flutter 應用。

混合開發大概包括幾種:

  • 第一種,混合棧,這是躲不掉的,怎樣把頁面混合起來,混合棧是這個架構圖裏的 Boost,已經開源了。

  • 第二種,工程化,從代碼管理到持續集成、打包、監控、高可用,這一串的鏈路。

  • 第三種,音視頻和資源一體化,這塊主要是考慮高性能圖片、視頻組件與 Native 複用。

1. 混合棧的關注點

(1)單頁面棧和多頁面棧。從 Native 角度看,當新開一個 Flutter 頁面,這個頁面要在新的 Activity(以 Android 爲例)中打開,仍是在同一個 Activity 裏面面來回切換 Flutter 頁面?若是每個 Flutter 頁面都掛在一個新的 Activity 下,就算多頁面棧的設計。反之就是單頁面棧。哪一種更好?若是想跟 Native 自己的棧可以很好結合,邏輯和順序都能徹底一致的話,那多頁面棧是最合適的。那單頁面棧適合用在什麼場景?好比在 Flutter 頁面彈出浮層或者 Dialog 的場景,這時直接在 Flutter 內部用 Navigator.push 就能夠達到想要的效果,可是除此以外,大多數場景都須要多頁面棧的方式。flutter_boost 這兩種都支持。

(2)單引擎和多引擎。上面說的多頁面棧涉及一個問題,每一個 Flutter 頁面都掛在一個新的 Activity(以 Android 爲例)下,那這些頁面的 Flutter 引擎究竟是用同一個引擎仍是多個引擎呢?這也是混合棧要考慮的重要問題。直觀來看,要適應多頁面棧,那就用多引擎,每一個頁面都起一個 Flutter 引擎最簡單了,但最大的問題是內存會暴漲,雖然如今官方也想去深度複用這個引擎,讓引擎可以很輕量化,可是目前這個尚未 Ready。因此從單、多引擎來說,目前咱們使用的方案仍是單引擎的方案。單引擎的方案的優勢就是內存省了,全部的 Flutter 都在一個引擎裏。

(3)單引擎雖然解決了內存暴漲問題,但因爲引擎內的棧跟外面 Native 棧的頁面生命週期不同,用單個 Flutter 引擎至關於把這個 Flutter 頁面在 Native 頁面間來回移動,Native 的生命週期跟 Flutter 生命週期要徹底對上,若是對很差的話就會出現轉場白屏、埋點丟失、頁面銷燬不當等各類奇怪的問題。對單引擎設計來說,實現基本流程一般沒問題,可是最重要的是裏面細節問題特別多,須要仔細處理各個細節問題。這方面你們能夠參考 flutter_boost。

2. 混合開發的工程化問題

這塊問題一般是由於原來有 Native 同窗,如今有 Flutter 的同窗,要一塊兒去開發。這個問題剛纔有一個同窗也提問到了,這時工程到底怎麼作?是用 Flutter 包 Native,仍是用 Native 包 Flutter?這是兩種思惟方式。標準的 Flutter 是在 Flutter 下面掛了安卓、iOS 的工程,用這樣 Flutter 包 Native 的方式。還有一種方式是能夠反過來,能夠把 Flutter add to Native 的思路,閒魚用這個思路作了一個 flutter_boot 工具,已經開源,你們有興趣也能夠參考。

3. 音視頻和資源一體化

閒魚原來在 Native 時期,有成熟的圖片庫,也有成熟的視頻組件作濾鏡、編解碼優化之類優化的,這些組件在 Flutter 上要複用起來,若是用官方默認的方式在生產環境會發現性能有問題。

這是關於視頻複用的方案,第一個官方直接給的默認實現,在沒有格式轉換的狀況下問題不大,但須要格式轉換時,性能就會有問題。以打開攝像頭作預覽的場景爲例,默認的第一種方案是把 Texture 提取到 Java 或者 Flutter 運行期的 Buffer 對象裏,每一幀經過 Channel 傳遞到 Flutter,再貼到 Texture 上,這個辦法缺點是有一次讀、一次寫的過程,性能不是最優的。從咱們的實測看,iPhone 7 上 720P 的視頻,這種方案平均每幀多消耗 5-6ms。

後來咱們嘗試兩種方案把性能提高,第一種方案的思路是「新方案 1」這樣的,想辦法把 Native 側的 Texture 跟 Flutter 側的 Texture 複用,直接傳遞 Texture ID,這個好處是沒有往上 Dump 內存的過程。這種方式很差的地方是須要改 Flutter 的代碼,打 Patch。還有一種更好的方案,這是咱們後來發現的,這種方案不用打 Patch,能夠用 Surface Texture 直接複用的機制,在 Native 和 Flutter 側是一樣的 Surface Texture,就能夠複用了,建議你們用這種方式更好一些。

2、規模化

工程變複雜了,頁面變多了,這時候重要關注的點是怎樣讓人員之間、模塊之間解耦。團隊大了並行開發更多,有幾個小組須要並行開發,再也不是一個大組了。另外,也要關注代碼標準化。

剛纔說到解耦,大概有兩個層面之間的解耦:一個是「Card」的解耦,長頁面中每一行是一個業務組件 Card, Card 之間讓它們儘可能有少的互相依賴。還有一部分是 Card 的內部解耦,主要是解 UI、邏輯和數據分離的問題,這個問題業界有一種 Redux 方案,閒魚作的 fish_redux 是把組件 Card 間的解耦能力加到了 Redux 上,即把一個大頁面拆成多個組件,組件間也能夠解耦解了。

規模化期另一個考慮的問題是高可用。Flutter 有一個特色,由 Flutter 引發的整個 APP 的 Crash 不多,更有效的方式是監控它的異常,對異常的監控就須要在這個階段把異常作聚類、信息裁剪。另外,還須要對頁面 FPS,可交互時長等的監控。

此外,CI 和自動化測試方面,在規模期也有一些事情要作,原來的自動化測試有一些沒有辦法用在 Flutter 上,例如基於 Instrumentation 和 UiAutomator 的測試方案。只有基於 Monkey 的能夠直接用。

這裏詳細介紹下解耦時用到的 AOP 技術,AOP 是解侵入的手段, JAVA 和 OC 裏這塊都容易,可是在 Dart 裏沒有成熟方案。AOP 的一種解決方案是基於代碼生成,另一種解決方案是在 Dill 裏修改,Dill 是 Dart 到最終產物中間的語言。

這塊的思路是什麼樣?原來有一個 APP 工程,你想對它加一些功能,這個用 AOP 方式切進來,讓原 APP 工程感知不到。解決方案是讓 aop-project 的 Main 引用 app-project 的 Main。打包時選生成 aop.dill,包含這兩個工程全部的代碼,再通過 aspectd 的處理,把兩個工程的切面編織到一塊兒,成爲最終產出物。

怎麼去改這個 aop.dill?Flutter 在編譯中有一個很好的設計,它提供轉換的能力,可讓你在編譯流程中拿到語法對象之後,對這個對象作本身的轉換。基於這樣的原理,讀取語法代碼的樹,而後對它作遍歷,把工程裏的 AOP 註解解析出來,編織到一塊兒就能夠了。作 AOP 時要考慮,好比以 foo() 函數爲例,是想在 foo 外面作界面插入,仍是在 foo 裏面第一行和末行插入,甚至還有一種多是在 foo 函數中間某一行作插入,這些都是 AOP 不一樣的編織動做,對應幾個操做分別有不一樣的註解。

3、一體化

第一個概念,動態模板是在業務層想作業務流程下沉時用到。好比一個交易鏈路裏有可定製的優惠模塊、物流模塊、商品信息模塊等,可能有很是多組件,能夠把這個流程沉澱,變成中臺,讓上層多個業務可以複用組裝,在這個主幹的流程上作本身的擴展點。咱們的一個解法是用動態模板的方式,讓 Flutter 支持模板化。

第二個概念是 Serverless 框架,能夠把服務端的業務層代碼跟客戶端代碼統一。

上面講的是一體化的端側的概念,下面這部分是服務端的容器,很重要的基礎設施是 FaaS 平臺和 ServiceMesh。標準的 Faas 不須要咱們本身去建,雲上和阿里內部都有,咱們要作的是讓 Dart 跑在 Faas 上,作一個符合 Faas 規範的 Dart 語言的 Runtime 容器,核心要解決什麼問題?一個是讓 Runtime 能適應不一樣的 Faas 平臺,它有足夠的解耦性。另一個重要的問題是解決語言無關性問題, Dart 語言確定碰到原來生態如 Java 或者某種語言的,像閒魚的是 Java 生態,那怎麼去調這些異構服務,要解決語言無關性的問題。大概思路跟 Servicemesh 差很少,用 Sidecar 解決語言無關性的問題。

中間是通道服務響應框架,這個框架主要是通道層的抽象,爲何要有通道層的抽象?原來是基於無線網關接口,如今基於 Faas,能夠把這個 Faas 當作一個本地接口去調,甚至能夠把 Faas 上跑的函數當作本地端的函數,這樣去調用。

一體化的演進過程:第一步,剛纔講的是一個邏輯下移的過程,把原來端側的邏輯寫到了 Faas 層,這就是邏輯下移的過程。第二步剛纔講的模板是屬於 SSR 範疇,屬於把渲染下移的過程。最後是遠景,把 UI 自動化生成掉。一體化的設想在沒用以前業務可能有不少疑惑,到底這種開發模式你們會不會接受、能不能真的帶來效率的提高,可是把這個推出來之後,咱們有幾個業務基於這個去作,效果仍是很是不錯的,把原來很是重的雲端邏輯歸一化了,例以下單頁的邏輯,客戶端下單要算優惠,組件聯動,端側有很重的邏輯,服務端也很重的邏輯,如今下單的業務同窗用一體化把這兩個邏輯歸一在一塊兒,都到 Faas 上解這個邏輯時,就變得可維護性好了不少。

有的同窗可能會疑惑一體化後,端上原來多是有狀態的,在 FaaS 上怎麼解決端上狀態的問題?

這個問題的解法有幾個思路,首先對於客戶端編程來說,咱們用框架把本地函數和 FaaS 函數統一封裝路由,須要訪問遠程的狀況纔會路由到遠程。另外,基於 Redux 的 Action 方式調用 FaaS,Action 中能夠先把客戶端完整 State 狀態傳到 Faas 上,Faas 函數能夠知道客戶端的完整狀態。有一些反作用是請求次數變多,對於這種特殊狀況須要注意。

一體化還有一個重要的問題是工程化歸一,使開發 FaaS 像在本地工程在寫客戶端代碼同樣。工程化歸一業界有開源軟件 Serverless Framework 能夠複用,亞馬遜、Google 雲在 Faas 層都有 CLI 工具組件,基於開源產品去打通工程一體化就容易一些。

回顧及展望

大概回顧一下今天要講的內容:

第一,閒魚選擇 Flutter 選擇的因素,給你們作參考,閒魚是在這樣幾個業務特色:獨立 APP、雙端一致性要求,蘋果用戶佔比高;APP 中以產品化頁面爲主。另外,團隊組成也是一個重要因素。

第二,閒魚架構演進過程,從引入期、到發展期、到一體化的探索,每一個階段要去考慮的問題和關鍵的技術點。

最後,展望接下來要作的:

Flutter 和 Faas 剛剛講的一體化這塊剛剛起步沒多久,有一些典型的業務場景落地了,可是尚未大規模的作更多鋪開。UI2code 嘗試。另外,咱們但願把 Flutter 基礎設施、基礎組件能力開放,能讓超大型的 APP 使用。最後,Flutter 的社區生態,要靠你們一塊兒共建和完善。

Q&A

提問:我是一名 iOS 開發者,目前在作跨平臺工做。跨平臺開發者都知道阿里繫有一個至關知名的 Weex,您也提到閒魚在 2016 年主要業務都是由 Weex 去承載的,咱們也有作 Weex 的相關工做,它有可以作到多端同構,你能夠自定義一些驅動,甚至部署到小程序上。從 Weex 更符合大前端、一體化的趨勢,Weex 在跨平臺以及原平生臺上都有至關的表現。個人問題是,做爲阿里嫡系跨平臺優秀的引擎,是什麼契機決定讓大家閒魚團隊全面擁抱 Flutter?可以給咱們一個更好的參考。

回答:這個問題是場景的問題。Weex 跟 Flutter 在跨端、動態性、性能方面的表現,是一個三角形。目前的技術框架都很難把這三要素都解決很是好,好比你剛纔說 Weex 很好,可以很完美的解決這三個問題,但其實 Flutter 的關鍵優點是在性能更好,比 Weex 好。Weex 的優點是它前端的生態和容器的普遍性以及它的動態性。說究竟是場景問題,閒魚 80%是主鏈路場景,更可能是性能這塊的考慮,只有 20%是屬於外投活動類的,這部分咱們用 Weex、H五、小程序作,主鏈路仍是更高性能的選擇。杭州那個創業公司例子也是這樣的,在 RN 和 Flutter 中選擇,最後敲定到了 Flutter 上。確實你們都有這個考慮,仍是看本身的場景想取哪方面。


提問:我以前看閒魚公衆號上有 UI2code,想看看大家最新的進展是怎樣的,UI2code 在閒魚 APP 上頁面實際使用的佔比率大概是怎樣的?

回答:咱們有個本身內部創業 APP,一個全新的 APP,是用 UI2Code 作的,效果就很好。我我的以爲 UI2code 目前技術水平適合作全新的 APP 或者全新的頁面。作老頁面最大的問題是修改、合併的問題。因此 UI2code 對全新 APP 頁面做用很大,由於那個所有是用這個生成的。目前這個技術更適用這樣的場景。


提問:大家用 Weex 差很少是最先的,咱們的 APP 也有 Weex。我想問的是大家對 Flutter 兼容 Weex 有相關的技術嗎?好比我以前有些業務是 Weex 作的,如今想在 Flutter 中去作,有相關的技術調研嗎?

回答:這兩個調用的話,其實它們兩個是獨立的,Weex 若是渲染到 Native 控件,就像標準 Native 跟 Flutter 是同樣的。可是 Weex 若是最後降級成 Web View 就變成 Web View 跟 Flutter 的問題,它們其實也是兼容的,如今 Flutter 上用 Web View 也是沒問題的,Web View 本質上也是 Native 的,因此最後都會落到 Native 和 Flutter 的關係問題。若是你想作控件極的關聯,好比 Weex 寫的控件跟 Flutter 寫的控件作交互,它們兩個能夠在同一個頁面出現,這樣就會比較難,目前沒法實現。此外,若是是想讓 Flutter 像 Weex 同樣有那麼靈活的動態性,目前也沒有太好的辦法。


提問:Faas 那一層是否是至關於把 Server 端數據庫的模型轉換成前端 View Model 的過程?另一個問題,Flutter 客戶端協同開發是有組件化方案?仍是有其餘的?好比開發過程當中調試的方案是什麼樣的?Flutter 多業務線同時開發一個 APP,有不少獨立的組件,在開發過程當中是怎樣調試的?好比運行在閒魚殼 APP 裏跑起來,經過什麼樣的方式可以跑起來?

回答:Faas 是否是指把服務端 DB 數據透出到客戶端的這個問題。大概能夠這樣理解,從業務開發分層角度看的話,有客戶端、中間業務膠水層,領域層。膠水層就是把各個領域的接口拿過來組裝,組裝成頁面要的數據,返回給客戶端。FaaS 作的是這一層,膠水層。

提問(續):至關於領域那一端接口仍是 Server 來維護,最後輸出更偏向於 Server 輸出的數據,客戶端須要配置東西的話須要在這層上再封裝一層、組裝一層客戶端須要的形式?

回答:FaaS 這一層就是上面說的膠水層,之前沒有 FaaS 時這一層是服務端同窗寫的,如今由客戶端同窗用 FaaS 來寫。Faas 這一層能夠理解爲前端一般說的 BFF 這一層,但實際上會比 BFF 的內容更多些,FaaS 解決的是煙囪式的邏輯,FaaS 的橫向間依賴比較少,一個請求進來,向下層拿數據把它組裝返回。領域層的區別是它有不少領域與領域間的橫向依賴,用的書據存儲也很是多,模型複雜,就不適合 FaaS,目前尚未用在 FaaS 上。

提問(續):怎樣判斷 Faas 具體運行在 Server 端仍是客戶端?

回答:其實 Faas 都是在 Server 端,只不過有一個基於 Action 的客戶端框架,讓客戶端開發時,本地邏輯和 Faas 邏輯都像寫一個函數同樣。咱們的想法是在 Faas 函數上加一個註解,剩下就都是同樣的了,是這樣的方向。


提問:我常常關注閒魚的一些技術文章,您剛纔提出兩個觀點,noUI 和動態模板下發。在 Flutter 中寫頁面時,有參考 Flutter Design 網站,把全部的組件進行動態拖拽生成靜態模板,Flutter 趨勢是否能夠發展成只關注數據集和業務邏輯,剩下都交給工具集或者工具去作,這樣就能夠很大一部分解放了 Flutter 寫頁面的生產力,閒魚有沒有這樣的考慮?或者提供這樣的編譯工具給社區來使用?

回答:這個其實在內部是有用的,可是目前尚未開放出來。你講的那種方式多是更多偏工具化的,用工具去生成這樣一個頁面,它的代碼仍是 Flutter 的代碼。這種方式閒魚有交集的是 UI2code,確實是直接生成 Flutter 代碼,倒也不是拿一個圖片徹底一會兒就生成了,也有你說的拖動過程,要作些修改、屬性簡單的設置,而後生成 Flutte 頁面代碼,這塊是能夠的,可是這塊也沒有開源。

更多精彩分享

Flutter 沙龍回顧 | 跨平臺技術趨勢及字節跳動 Flutter 架構實踐

Flutter 沙龍回顧 | 如何縮減接近 50% 的 Flutter 包體積

Flutter 沙龍回顧 | Custom Widgets in Flutter

上海沙龍回顧 | 字節跳動在Spark SQL上的核心優化實踐

字節跳動技術沙龍

字節跳動技術沙龍是由字節跳動技術學院發起,字節跳動技術學院、掘金技術社區聯合主辦的技術交流活動。

字節跳動技術沙龍邀請來自字節跳動及業內互聯網公司的技術專家,分享熱門技術話題與一線實踐經驗,內容覆蓋架構、大數據、前端、測試、運維、算法、系統等技術領域。

字節跳動技術沙龍旨在爲技術領域人才提供一個開放、自由的交流學習平臺,幫助技術人學習成長,不斷進階。


歡迎關注「字節跳動技術團隊」

相關文章
相關標籤/搜索