本文由阿里闲鱼技术团队祈晴分享,原文参考微信公众号淘系技术,感激作者的技术分享。
OpenIMgithub 开源地址:
https://github.com/OpenIMSDK/…
OpenIM 官网:https://www.rentsoft.cn
OpenIM 官方论坛:https://forum.rentsoft.cn/
现状
闲鱼 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 路上会始终放弃前沿摸索,最初能反馈到生态圈;总结一句话,摸索过程在于你敢于迈出第一步,前面才会一直惊喜发现。
更多原创技术文章:
开源 OpenIM:高性能、可伸缩、易扩大的即时通讯架构
https://forum.rentsoft.cn/thr…
【OpenIM 原创】简略轻松入门 一文解说 WebRTC 实现 1 对 1 音视频通信原理
https://forum.rentsoft.cn/thr…
【OpenIM 原创】开源 OpenIM:轻量、高效、实时、牢靠、低成本的音讯模型
https://forum.rentsoft.cn/thr…
OpenIM 服务发现和负载平衡 golang 插件:gRPC 接入 etcdv3
https://forum.rentsoft.cn/thr…
【OpenIM 原创】简略轻松入门 一文解说 WebRTC 实现 1 对 1 音视频通信原理
https://forum.rentsoft.cn/thr…
【OpenIM 原创】C/C++ 调用 golang 函数,golang 回调 C /C++ 函数
https://forum.rentsoft.cn/thr…