阿里技術分享:閒魚IM基於Flutter的移動端跨端改造實踐

JackJiang發表於2021-07-05

本文由阿里閒魚技術團隊祈晴分享,本次有修訂和改動,感謝作者的技術分享。

1、內容概述

本文總結了阿里閒魚技術團隊使用Flutter在對閒魚IM進行移動端跨端改造過程中的技術實踐等,文中對比了傳統Native與現在大熱的Flutter跨端方案在一些主要技術實現上的差異,以及針對Flutter技術特點的具體技術實現,值得同樣準備使用Flutter開發IM的技術同行們借鑑和參考。

學習交流:

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

- 開源IM框架原始碼:github.com/JackJiang20…

2、閒魚IM現狀

閒魚IM的移動端框架構建於2016至2017年間,期間經過多次迭代升級導致歷史包袱累積多,後面又經歷IM介面的Flutter化,從而造成了客戶端架構愈加複雜。

從開發層面總結閒魚IM移動端當前架構主要存在如下幾個問題:

  • 1)研發效率較低:當前架構涉及到Android/iOS雙端的邏輯程式碼以及Flutter的UI程式碼,定位問題往往只能從Flutter UI表相倒查到Native邏輯層;
  • 2)架構層次較差:架構設計上分層不清晰,業務邏輯夾雜在核心的邏輯層致使程式碼變更風險大;
  • 3)效能測試略差:核心資料來源儲存Native記憶體,需經Flutter Plugin將資料來源序列化上拋Flutter側,在大批量資料來源情況下效能表現較差。

從產品層面總結閒魚IM移動端當前架構的主要問題如下:

  • 1)定位問題困難:線上輿情反饋千奇百怪,測試始終無法復現相關場景,因此很多時候只能靠現象猜測本質;
  • 2)疑難雜症較多:架構的不穩定性造成出現的問題反覆出現,當前疑難雜症主要包括未讀紅點計數、iPhone5C低端機以及多媒體傳送等多個問題;
  • 3)問題差異性大:Android和iOS兩端邏輯程式碼差異大,包括埋點邏輯都不盡相同,排查問題根源時雙端都會有不同根因,解決方案也不相同。

3、業界的移動端跨端方案

為解決當前IM的技術痛點,閒魚今年特起關於IM架構升級專案,重在解決客戶端中Andriod和iOS雙端一致性的痛點,初步設想方案就是實現跨端統一的Android/iOS邏輯架構。

在當前行業內跨端方案可初步歸類如下圖架構:

在GUI層面的跨端方案有WeexReactNative、H5、Uni-APP等,其記憶體模型大多需要通過橋接到Native模式儲存。

在邏輯層面的跨端方案大致有C/C++等與虛擬機器無關語言實現跨端,當然組合語言也可行。

此外有兩個獨立於上述體系之外的架構就是Flutter和KMM(谷歌基於Kotlin實現類似Flutter架構),其中Flutter執行特定DartVM,將記憶體資料掛載其自身的isolate中。

考慮閒魚是Flutter的前沿探索者,方案上優先使用Flutter。然而Flutter的isolate更像一個程式的概念(底層實現非使用程式模式),相比Android,同一程式場景中,Android的Dalvik虛擬機器多個執行緒執行共享一個記憶體Heap,而DartVM的Isolate執行隔離各自的Heap,因而isolate之間通訊方式比較繁瑣(需經過序列化反序列化過程)。

整個模型如下圖所示:

若按官方混合架構實現Flutter應用,開啟多個FlutterAcitivty/FlutterController,底層會生成多個Engine,對應會存在多個isolate,而isolate通訊類似於程式通訊(類似socket或AIDL),這裡借鑑閒魚FlutterBoost的設計理念,FlutterIM架構將多個頁面的Engine共享,則記憶體模型就天然支援共享讀取。

原理圖如下:

4、閒魚IM基於Flutter的架構設計

4.1 新老架構對比

**如下圖所示:**是一個老架構方案,其核心問題主要集中於Native邏輯抽象差,其中邏輯層面還設計到多執行緒併發使得問題倍增,Android/iOS/Flutter互動繁雜,開發維護成本高,核心層耦合較為嚴重,無插拔式概念.

考慮到歷史架構的問題,演進如下新架構設計:

如上圖所示,架構從上至下依次為:

  • 1)業務層;
  • 2)分發層;
  • 3)邏輯層;
  • 4)資料來源層。

資料來源層來源於推送或網路請求,其封裝於Native層,通過Flutter外掛將訊息協議資料上拋到Flutter側的核心邏輯層,處理完成後變成Flutter DB的Enitity實體,實體中掛載一些訊息協議實體。

核心邏輯層將繁雜資料扁平化打包掛載到分發層中的會話記憶體模型資料或訊息記憶體模型資料,最後通過觀察者模式的訂閱分發到業務邏輯中。

Flutter IM重點集中改造邏輯層和分發層,將IM核心邏輯和業務層面資料模型進行封裝隔離,核心邏輯層和資料庫互動後將資料封裝到分發層的moduleData中,通過訂閱方式分發到業務層資料模型中。

此外在IM模型中DB也是重點依賴的,個人對DB資料庫管理進行全面封裝解,實現一種輕量級,效能佳的Flutter DB管理框架。

4.2 DB儲存模型

Flutter IM架構的DB儲存依賴資料庫外掛,目前主流外掛是Sqflite

其儲存模型如下:

依據上圖Sqflite外掛的DB儲存模型會有2個等待佇列:

  • 一個是Flutter層同步執行佇列;
  • 一個是Native層的執行緒執行佇列。

其Android實現機制是HandlerThread,因此Query/Save讀寫在會同一執行緒佇列中,導致響應速度慢,容易造成DB SQL堆積,此外缺失快取模型。

於是個人定製如下改進方案:

Flutter側通過表的主鍵設計查詢時候會優先從Entity Cache層去獲取,若快取不存在,則通過Sqflite外掛查詢。

同時改造Sqflite外掛成支援sync/Async同步非同步兩種方式操作,對應到Native側也會有同步執行緒佇列和非同步執行緒佇列,保證資料吞吐率。但是這裡建議查詢使用非同步,儲存使用同步更穩妥,主要怕出現多個相同的資料元model同一時間進入非同步執行緒池中,儲存先後順序無法有效的保證。

4.3 ORM資料庫方案

IM架構重度依賴DB資料庫,而當前業界還沒有一個完備的資料庫ORM管理方案,參考了Android的OrmLite/GreenDao,個人自行設計一套Flutter ORM資料庫管理方案。

其核心思想如下:

由於Flutter不支援反射,因此無法直接像Android的開源資料庫方式操作,但可通過APT方式,將Entity和Orm Entity繫結於一身,操作OrmEntity即操作Entity,整個程式碼風格設計也和OrmLite極其相似。

參考程式碼如下:

4.4 IM記憶體資料模型

基於Flutter的IM移動端架構在記憶體資料模型主要劃分為會話和訊息兩個顆粒度:

  • 1)會話記憶體資料模型交託於SessionModuleData:會話記憶體資料有一個根節點RootNotice,然後其掛載PSessionMessageNotice(這裡PSessionMessageNotice是ORM對映的會話DB表模型)子節點集合。
  • 2)訊息記憶體資料模型交託於MessageModuleData:訊息記憶體資料會有一個MessageConatiner容器管理,其內部掛載此會話中的PMessage(PMessage是ORM對映的訊息DB表模型)訊息集合。

依據上一章節,PSessionMessageNotice設計了一個OrmEnitity Cache,考慮到IM中會話數是有限的,因此PSessionMessageNotice都是直接快取到Cache中。

這種做法的好處是各地去拿會話資料元時候都是快取中同一個物件,容易保證多次重複讀寫的資料一致性。而PSessionMessageNotice考慮到其數量可以無限多的特殊性,因此這裡將其掛載到MessageContainer的記憶體管理中,在退出會話的時機會校驗容器中PMessage集合的數量,適當縮容可以減少記憶體開銷。

模型如下圖所示:

4.5 狀態管理方案

基於Flutter的IM移動端架構狀態管理方案比較簡單,對資料來源Session/Message維度使用觀察者模式的訂閱分發方式實現,架構類似於EventBus模式,頁面級的狀態管理無論使用fish-redux、scopeModel或者provider幾乎影響面不大,核心還是需保留一種插拔式抽象更重要。

架構如下圖:

4.6 IM同步模型方案

當前現狀的訊息同步模型:

如上圖所示是,模型中存在ACCS Thread/Main Thread/Region Thread等多執行緒併發場景,導致易出現多執行緒高併發的問題。

native的推送和網路請求同步的隔離方案通過Lock的鎖機制,並且通過佇列降頻等方式處理,流程繁瑣且易出錯。整體通過Region Version Gap去判斷是否有域空洞,進而執行域同步補充資料。

改進的同步模型如下:

如上圖所示,在Flutter側天然沒多執行緒場景,通過一種標記位的轉化同步非同步實現類似Handler訊息佇列,架構清晰簡約了很多,避免鎖帶來的開銷以及同步問題。

5、本次改造進展以及效能對比

1)針對架構層面:

在基於Flutter的IM架構中,重點將雙端邏輯差異性統一成同一份Dart程式碼,完全磨平Android/iOS的程式碼差異性帶來的問題。

帶來的好處很明顯:

  • 1)降低開發維護、測試迴歸、視覺驗收的一半成本,極大提高研發效率;
  • 2)架構上進行重構分層,實現一種解耦合,插拔式的IM架構;
  • 3)同時Native到Flutter側的大量資料上拋序列化過程改造程Flutter引用傳遞,解決極限測試場景下的私聊卡頓問題。

2)針對線上輿情:

  • 1)補齊UT和TLog的集團日誌方式做到可追蹤,可排查;
  • 2)針對於很多現存的疑難雜症重點集中專項解決,比如iphone5C的架構在Flutter側統一規劃;
  • 3)未讀紅點計數等問題也在架構模型升級中修復;
  • 4)此外多媒體音視訊傳送模組進行改造升級。

3)效能資料對比:

當IM架構的邏輯層和UI層都切換成Flutter後,和原先架構模式初步對比,整體記憶體水位持平。

其中:

  • 1)私聊場景下小米9測試結構記憶體下降40M,功耗降低4mah,CPU降低1%;
  • 2)極限測試場景下新架構記憶體資料相比於舊架構有一個較為明顯的改觀(主要由於兩個介面都使用Flutter場景下,頁面切換的開銷降低很多)。

6、未來展望

JS跨端不安全,C++跨端成本有點高,Flutter會是一個較好選擇。彼時閒魚FlutterIM架構升級根本目的從來不是因Flutter而Flutter,是由於歷史包袱的繁重,程式碼層面的維護成本高,新業務的擴充套件性差,人力配比不協調以及疑難雜症的輿情持續反饋等等因素造成我們不得不去探索新方案。

經過閒魚IM超複雜業務場景驗證Flutter模式的邏輯跨端可行性,閒魚在Flutter路上會一直保持前沿探索,最後能反饋到生態圈。

總結一句話,探索過程在於你勇於邁出第一步,後面才會不斷驚喜發現。(原文連結:點此進入,本次有修訂和改動)

本文已同步釋出於:www.52im.net/thread-3615…

相關文章