阿里技术分享:闲鱼 IM 基于 Flutter 的移动端跨端改造实践
本文由阿里闲鱼技术团队祈晴分享,本次有修订和改动,感谢作者的技术分享。
1、内容概述
本文总结了阿里闲鱼技术团队使用 Flutter 在对闲鱼 IM 进行移动端跨端改造过程中的技术实践等,文中对比了传统 Native 与现在大热的 Flutter 跨端方案在一些主要技术实现上的差异,以及针对 Flutter 技术特点的具体技术实现,值得同样准备使用 Flutter 开发 IM 的技术同行们借鉴和参考。
学习交流:
- 移动端 IM 开发入门文章:《新手入门一篇就够:从零开发移动端IM》
- 开源 IM 框架源码:https://github.com/JackJiang2011/MobileIMSDK
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 层面的跨端方案有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 共享,则内存模型就天然支持共享读取。
原理图如下:
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 路上会一直保持前沿探索,最后能反馈到生态圈。
总结一句话,探索过程在于你勇于迈出第一步,后面才会不断惊喜发现。
(原文链接:点此进入,本次有修订和改动)
附录:更多文章汇总
[1] 更多阿里团队的文章分享:
《阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处》
《阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路》
《来自阿里OpenIM:打造安全可靠即时通讯服务的技术实践分享》
《钉钉——基于IM技术的新一代企业OA平台的技术挑战(视频+PPT) [附件下载]》
《阿里技术结晶:《阿里巴巴Java开发手册(规约)-华山版》[附件下载]》
《重磅发布:《阿里巴巴Android开发手册(规约)》[附件下载]》
《阿里技术分享:电商IM消息平台,在群聊、直播场景下的技术实践》
《阿里技术分享:闲鱼IM基于Flutter的移动端跨端改造实践》
[2] 更多 IM 开发综合文章:
《移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”》
《移动端IM开发者必读(二):史上最全移动弱网络优化方法总结》
《现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障》
《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》
《开源IM工程“蘑菇街TeamTalk”的现状:一场有始无终的开源秀》
《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》
《子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践》
《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》
《自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)》
《IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!》
《适合新手:从零开发一个IM服务端(基于Netty,有完整源码)》
《适合新手:手把手教你用Go快速搭建高性能、可扩展的IM系统(有源码)》
《IM里“附近的人”功能实现原理是什么?如何高效率地实现它?》
《IM“扫一扫”功能很好做?看看微信“扫一扫识物”的完整技术实现》
《IM消息ID技术专题(一):微信的海量IM聊天消息序列号生成实践(算法原理篇)》
《IM消息ID技术专题(二):微信的海量IM聊天消息序列号生成实践(容灾方案篇)》
《IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略》
《IM消息ID技术专题(四):深度解密美团的分布式ID生成算法》
《IM消息ID技术专题(五):开源分布式ID生成器UidGenerator的技术实现》
《IM消息ID技术专题(六):深度解密滴滴的高性能ID生成器(Tinyid)》
《IM开发宝典:史上最全,微信各种功能参数和逻辑规则资料汇总》
《IM开发干货分享:我是如何解决大量离线消息导致客户端卡顿的》
《零基础IM开发入门(四):什么是IM系统的消息时序一致性?》
《IM开发干货分享:有赞移动端IM的组件化SDK架构设计实践》
《一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等》
《IM扫码登录技术专题(一):微信的扫码登录功能技术原理调试分析》
《IM扫码登录技术专题(二):市面主流的扫码登录技术原理调试分析》
《IM扫码登录技术专题(三):通俗易懂,IM扫码登录功能详细原理一篇就够》
《理解IM消息“可靠性”和“一致性”问题,以及解决方案探讨》
《阿里技术分享:闲鱼IM基于Flutter的移动端跨端改造实践》
>> 更多同类文章 ……
本文已同步发布于“即时通讯技术圈”公众号。
评论