Flutter IM 跨端架構設計和實現

現狀


閒魚 IM 框架構建於 2016-2017 年,期間多次迭代升級導致歷史包袱累積多,後經 IM 界面 Flutter 化,造成架構更復雜,開發層面總結閒魚當前架構主要存在如下幾個問題:

• 研發效率較低:當前架構開發需求涉及到 Android/iOS 雙端的邏輯代碼以及 Flutter 的 UI 界面代碼,定位問題往往只能從 Flutter UI 表象追查到 Native 邏輯漏洞;

• 架構層次較差:架構設計上分層不清晰,業務邏輯夾雜在覈心的邏輯層致使代碼變更風險大;

• 性能測試略差:核心數據源存儲 Native 內存,需經 Flutter Plugin 將數據源序列化上拋 Flutter 側,在大批量數據源情況下性能表現較差;

從輿情層面總結閒魚 IM 當前架構的主要問題如下:

• 定位問題困難:線上輿情反饋千奇百怪,測試始終無法復現相關場景,因此很多時候只能靠現象猜測本質;

• 疑難雜症較多:架構不穩定性造成出現的問題反覆出現,當前疑難雜症主要包括未讀紅點計數,iPhone5C 低端機器架構,以及多媒體發送等多個問題;

• 問題差異性大:Android 和 iOS 兩端邏輯代碼差異大,包括現存埋點邏輯都不盡相同,因此排查問題根源時候雙端都會有不同問題根因,解決問題方案也不相同;

業界跨端方案

爲解決當前 IM 痛點,閒魚今年特起關於 IM 架構升級項目,重在解決客戶端中雙端一致性痛點,初步設想方案就是實現跨端統一的 Android/iOS 邏輯架構;在當前行業內跨端方案可初步歸類如下圖架構,在 GUI 層面的跨端方案有 Weex,ReactNative,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 共享,則內存模型就天然支持共享讀取,原理圖如下:

Flutter IM 架構設計

新老架構對比

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

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

架構從上至下依次爲業務層分發層邏輯層以及數據源層,數據源層來源於推送或網絡請求,其封裝於 Native 層,通過 Flutter 插件將消息協議數據上拋到 Flutter 側的核心邏輯層,處理完成後變成 Flutter DB 的 Enitity 實體,實體中掛載一些消息協議實體;核心邏輯層將繁雜數據扁平化打包掛載到分發層中的會話內存模型數據或消息內存模型數據,最後通過觀察者模式的訂閱分發到業務邏輯中;Flutter IM 重點集中改造邏輯層和分發層,將 IM 核心邏輯和業務層面數據模型進行封裝隔離,核心邏輯層和數據庫交互後將數據封裝到分發層的 moduleData 中,通過訂閱方式分發到業務層數據模型中;此外在 IM 模型中 DB 也是重點依賴的,個人對 DB 數據庫管理進行全面封裝解,實現一種輕量級,性能佳的 Flutter DB 管理框架;

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 同一時間進入異步線程池中,存儲先後順序無法有效的保證;

ORM 數據庫方案

IM 架構重度依賴 DB 數據庫,而當前業界還沒有一個完備的數據庫 ORM 管理方案,參考了 Android 的 OrmLite/GreenDao,個人自行設計一套 Flutter ORM 數據庫管理方案,其核心思想如下:

由於 Flutter 不支持反射,因此無法直接像 Android 的開源數據庫方式操作,但可通過 APT 方式,將 Entity 和 Orm Entity 綁定於一身,操作 OrmEntity 即操作 Entity,整個代碼風格設計也和 OrmLite 極其相似,參考代碼如下:

IM 內存數據模型

FlutterIM 架構在內存數據模型主要劃分爲會話和消息兩個顆粒度,會話內存數據模型交託於 SessionModuleData,消息內存數據模型交託於 MessageModuleData;會話內存數據有一個根節點 RootNotice,然後其掛載 PSessionMessageNotice(這裏 PSessionMessageNotice 是 ORM 映射的會話 DB 表模型)子節點集合;消息內存數據會有一個 MessageConatiner 容器管理,其內部掛載此會話中的 PMessage(PMessage 是 ORM 映射的消息 DB 表模型) 消息集合。

依據上一章節,PSessionMessageNotice 設計了一個 OrmEnitity Cache,考慮到 IM 中會話數是有限的,因此 PSessionMessageNotice 都是直接緩存到 Cache 中,這種做法的好處是各地去拿會話數據元時候都是緩存中同一個對象,容易保證多次重複讀寫的數據一致性;而 PSessionMessageNotice 考慮到其數量可以無限多的特殊性,因此這裏將其掛載到 MessageContainer 的內存管理中,在退出會話的時機會校驗容器中 PMessage 集合的數量,適當縮容可以減少內存開銷,模型如下圖所示:

狀態管理方案

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

IM 同步模型方案

如下是當前現狀的消息同步模型,模型中存在 ACCS Thread/Main Thread/Region Thread 等多線程併發場景,導致易出現多線程高併發的問題;native 的推送和網絡請求同步的隔離方案通過 Lock 的鎖機制,並且通過隊列降頻等方式處理,流程繁瑣且易出錯。整體通過 Region Version Gap 去判斷是否有域空洞,進而執行域同步補充數據。

改進的同步模型如下,在 Flutter 側天然沒多線程場景,通過一種標記位的轉化同步異步實現類似 Handler 消息隊列,架構清晰簡約了很多,避免鎖帶來的開銷以及同步問題。

進展以及性能對比

• 針對架構層面:在 FlutterIM 架構中,重點將雙端邏輯差異性統一成同一份 Dart 代碼,完全磨平 Android/iOS 的代碼差異性帶來的問題,降低開發維護,測試迴歸,視覺驗收的一半成本,極大提高研發效率;架構上進行重構分層,實現一種解耦合,插拔式的 IM 架構;同時 Native 到 Flutter 側的大量數據上拋序列化過程改造成 Flutter 引用傳遞,解決極限測試場景下的私聊卡頓問題;

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

• 性能數據對比:當 IM 架構的邏輯層和 UI 層都切換成 Flutter 後,和原先架構模式初步對比,整體內存水位持平,其中私聊場景下小米 9 測試結果內存下降 40M,功耗降低 4mah,CPU 降低 1%;極限測試場景下新架構內存數據相比於舊架構有一個較爲明顯的改觀,主要由於兩個界面都使用 Flutter 場景下,頁面切換的開銷降低很多;

展望

JS 跨端不安全,C++ 跨端成本有點高,Flutter 會是一個較好選擇;彼時閒魚 FlutterIM 架構升級根本目的從來不是因 Flutter 而 Flutter,是由於歷史包袱的繁重,代碼層面的維護成本高,新業務的擴展性差,人力配比不協調以及疑難雜症的輿情持續反饋等等因素造成我們不得不去探索新方案。經過閒魚 IM 超複雜業務場景驗證 Flutter 模式的邏輯跨端可行性,閒魚在 Flutter 路上會一直保持前沿探索,最後能反饋到生態圈;總結一句話,探索過程在於你勇於邁出第一步,後面纔會不斷驚喜發現

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ftZdQ-0GeMgr6kL9bZLA8w