本文由阿里閒魚技術團隊祈晴分享,本次有修訂和改動,感謝做者的技術分享。html
本文總結了阿里閒魚技術團隊使用Flutter在對閒魚IM進行移動端跨端改造過程當中的技術實踐等,文中對比了傳統Native與如今大熱的Flutter跨端方案在一些主要技術實現上的差別,以及針對Flutter技術特色的具體技術實現,值得一樣準備使用Flutter開發IM的技術同行們借鑑和參考。react
學習交流:git
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》github
- 開源IM框架源碼:github.com/JackJiang20…數據庫
閒魚IM的移動端框架構建於2016至2017年間,期間通過屢次迭代升級致使歷史包袱累積多,後面又經歷IM界面的Flutter化,從而形成了客戶端架構越發複雜。redux
從開發層面總結閒魚IM移動端當前架構主要存在以下幾個問題:緩存
從產品層面總結閒魚IM移動端當前架構的主要問題以下:安全
爲解決當前IM的技術痛點,閒魚今年特起關於IM架構升級項目,重在解決客戶端中Andriod和iOS雙端一致性的痛點,初步設想方案就是實現跨端統一的Android/iOS邏輯架構。markdown
在當前行業內跨端方案可初步歸類以下圖架構:weex
在GUI層面的跨端方案有Weex、ReactNative、H五、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共享,則內存模型就自然支持共享讀取。
原理圖以下:
**以下圖所示:**是一個老架構方案,其核心問題主要集中於Native邏輯抽象差,其中邏輯層面還設計到多線程併發使得問題倍增,Android/iOS/Flutter交互繁雜,開發維護成本高,核心層耦合較爲嚴重,無插拔式概念.
考慮到歷史架構的問題,演進以下新架構設計:
如上圖所示,架構從上至下依次爲:
數據源層來源於推送或網絡請求,其封裝於Native層,經過Flutter插件將消息協議數據上拋到Flutter側的核心邏輯層,處理完成後變成Flutter DB的Enitity實體,實體中掛載一些消息協議實體。
核心邏輯層將繁雜數據扁平化打包掛載到分發層中的會話內存模型數據或消息內存模型數據,最後經過觀察者模式的訂閱分發到業務邏輯中。
Flutter IM重點集中改造邏輯層和分發層,將IM核心邏輯和業務層面數據模型進行封裝隔離,核心邏輯層和數據庫交互後將數據封裝到分發層的moduleData中,經過訂閱方式分發到業務層數據模型中。
此外在IM模型中DB也是重點依賴的,我的對DB數據庫管理進行全面封裝解,實現一種輕量級,性能佳的Flutter DB管理框架。
Flutter IM架構的DB存儲依賴數據庫插件,目前主流插件是Sqflite。
其存儲模型以下:
依據上圖Sqflite插件的DB存儲模型會有2個等待隊列:
其Android實現機制是HandlerThread,所以Query/Save讀寫在會同一線程隊列中,致使響應速度慢,容易形成DB SQL堆積,此外缺失緩存模型。
因而我的定製以下改進方案:
Flutter側經過表的主鍵設計查詢時候會優先從Entity Cache層去獲取,若緩存不存在,則經過Sqflite插件查詢。
同時改造Sqflite插件成支持sync/Async同步異步兩種方式操做,對應到Native側也會有同步線程隊列和異步線程隊列,保證數據吞吐率。可是這裏建議查詢使用異步,存儲使用同步更穩妥,主要怕出現多個相同的數據元model同一時間進入異步線程池中,存儲前後順序沒法有效的保證。
IM架構重度依賴DB數據庫,而當前業界尚未一個完備的數據庫ORM管理方案,參考了Android的OrmLite/GreenDao,我的自行設計一套Flutter ORM數據庫管理方案。
其核心思想以下:
因爲Flutter不支持反射,所以沒法直接像Android的開源數據庫方式操做,但可經過APT方式,將Entity和Orm Entity綁定於一身,操做OrmEntity即操做Entity,整個代碼風格設計也和OrmLite極其類似。
參考代碼以下:
基於Flutter的IM移動端架構在內存數據模型主要劃分爲會話和消息兩個顆粒度:
依據上一章節,PSessionMessageNotice設計了一個OrmEnitity Cache,考慮到IM中會話數是有限的,所以PSessionMessageNotice都是直接緩存到Cache中。
這種作法的好處是各地去拿會話數據元時候都是緩存中同一個對象,容易保證屢次重複讀寫的數據一致性。而PSessionMessageNotice考慮到其數量能夠無限多的特殊性,所以這裏將其掛載到MessageContainer的內存管理中,在退出會話的時機會校驗容器中PMessage集合的數量,適當縮容能夠減小內存開銷。
模型以下圖所示:
基於Flutter的IM移動端架構狀態管理方案比較簡單,對數據源Session/Message維度使用觀察者模式的訂閱分發方式實現,架構相似於EventBus模式,頁面級的狀態管理不管使用fish-redux、scopeModel或者provider幾乎影響面不大,核心仍是需保留一種插拔式抽象更重要。
架構以下圖:
當前現狀的消息同步模型:
如上圖所示是,模型中存在ACCS Thread/Main Thread/Region Thread等多線程併發場景,致使易出現多線程高併發的問題。
native的推送和網絡請求同步的隔離方案經過Lock的鎖機制,而且經過隊列降頻等方式處理,流程繁瑣且易出錯。總體經過Region Version Gap去判斷是否有域空洞,進而執行域同步補充數據。
改進的同步模型以下:
如上圖所示,在Flutter側自然沒多線程場景,經過一種標記位的轉化同步異步實現相似Handler消息隊列,架構清晰簡約了不少,避免鎖帶來的開銷以及同步問題。
1)針對架構層面:
在基於Flutter的IM架構中,重點將雙端邏輯差別性統一成同一份Dart代碼,徹底磨平Android/iOS的代碼差別性帶來的問題。
帶來的好處很明顯:
2)針對線上輿情:
3)性能數據對比:
當IM架構的邏輯層和UI層都切換成Flutter後,和原先架構模式初步對比,總體內存水位持平。
其中:
JS跨端不安全,C++跨端成本有點高,Flutter會是一個較好選擇。彼時閒魚FlutterIM架構升級根本目的歷來不是因Flutter而Flutter,是因爲歷史包袱的繁重,代碼層面的維護成本高,新業務的擴展性差,人力配比不協調以及疑難雜症的輿情持續反饋等等因素形成咱們不得不去探索新方案。
通過閒魚IM超複雜業務場景驗證Flutter模式的邏輯跨端可行性,閒魚在Flutter路上會一直保持前沿探索,最後能反饋到生態圈。
總結一句話,探索過程在於你敢於邁出第一步,後面纔會不斷驚喜發現。(原文連接:點此進入,本次有修訂和改動)
本文已同步發佈於:www.52im.net/thread-3615…