苏宁最早出于办公的需要,考虑到外网访问权限控制及企业数据安全,没有采用 QQ、微信这类公有云部署的聊天软件,而是采购了 IBM 的 ST 在公司内做私有化部署。随着日常办公中企业定制化需求的深入,业务部门对 IM 和 OA 一体化诉求的日益凸显,ST 逐渐无法胜任,于是苏宁自研了一套 IM 系统。
苏宁产品定位的发展历程,从基础通讯,到企业办公、社交一体化,再到纯对内办公用途,最终服务于内同时对外提供一体化工作平台。系统架构也随着产品定位和产品架构的改变而改变,经历了从基于 XMPP 开源框架搭建的 1.0 系统到纯自研的 2.0 系统,直至目前正在研发的办公一体化、多活高可用、组件化、可轻量化灵活部署的 3.0 系统。
上图所示的是苏宁构建于 XMPP 协议系统的实现原理。在实现上每个独立的 Node Server 都包含完整的业务逻辑模块,服务器和服务器之间均建有长连接通道用于交换报文。通过消息的已达已读回执,来确保消息的可靠传输。
1. 由于 Node 之间均需维持长连接,随着服务端的横向扩展,长连接数是 N*(N-1),增长非常快。
2. 每台服务器都部署全量的服务,在部署上是种浪费,也不利于单个服务的升级和维护。
3.XMPP 报文按照 xml 格式定义,三种通讯原语 message、presence 和 iq 的无效载荷非常重,按以下所示,即使不携带任何信息,头部至少也是上百字节(1 字符 =2 字节),如果用 iq 实现业务层面的心跳,那也是上百字节。
4.XMPP 框架下的标准聊天消息以 id 为关键字,已经在本地的消息,往往会在对服务端的历史消息请求中,再次被拉取,无法做到按需增补、查漏补缺。究其本质,是这种消息结构的设置,无法完成增量比对。
为了解决 1.0 系统的上述问题,同时支持高可用、支持 SaaS 多企业,苏宁开发了自研 2.0 系统。
高可用的基本要求是无单点故障,基本方法是分层 (分而治之) 与冗余 (失效转移),苏宁采取了以下措施:
2.0 系统业务逻辑层面的最大变化在于对消息的处理。因为 IM 系统的核心问题就是要解决聊天消息的离线和在线处理,解决好这个,系统问题就等于解决了一半。
1. 移除离线缓冲,变离线消息推送为上线后终端的按需拉取,确保离线消息在各个客户端上均能被同步、按需补充。
1. 除消息的 uuid(全局唯一标识)和 ts(时间戳)以外,给消息定义 long 类型的 seq(序列号),按会线 开始必须连续。服务端需保证任意两条同一会线 互为充要条件;
2. 终端上线后,由终端主动获取最近会话列表,从而得到每个会话的最大 seq。在某个会话需要展示消息时,检查该会话当前的本地消息区间,向消息服务端请求本地缺失的消息,请求以 seq 区间为参数发起批量请求。
3. 终端在收到推送消息后,如果推送消息的 seq 与会话的最后一条消息的 seq 不连续,则先把消息加入到对应的缓冲窗口,并启动定时器。在收到后续的推送消息时,先检查缓冲窗口,把连续的一批消息退出窗口并写入数据库、展示给用户。如果超过定时器的时长,消息 seq 还未接续,则说明消息在传输过程中发生丢失,不再等待,一次性把窗口中的消息写入数据库并展示。丢失消息的补充,则通过前述的终端主动拉取来实现。
1. 移除了离线 改为请求最近会线 返回最近会线 改为上述技术方案图示的按需拉取
由于用户对 IM 系统的体验是通过终端来感知的,所谓的高性能,最终还是要靠终端的算法来完成。
4. 如果数据库使用的是系统默认的 SQLite,要避免在主线程直接进行任何数据库操作(增删改查),因为 SQLite 并发锁的颗粒度是文件级别的,会导致后台线程的数据库操作阻塞前台的。一方面,我们要识别和优化端上最慢的数据库操作的耗时,另一方面,全在后台操作数据库,界面就不会随机卡顿。
5. 在 UI 层还涉及到要防跳帧(用户可感知的 60 帧 / 秒 --16ms/ 帧)。这需要持续关注和简化各个 UI 布局的嵌套层次,从而减少布局尺寸计算和渲染的开销。另外在 android 端需要特别关注内存抖动引发的跳帧:避免高频调用的方法中分配局部大对象频繁触发大 GC,从而挂起主线 了,一时大脑堵塞了。
移动办公已成为企业提效、降成本的有效手段之一,在企业建设移动信息化的过程中,存在以下建设痛点:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。