导读
移动互联网时代,随着社交媒体、移动支付、线上购物等行业的快速发展,对即时通讯功能的需求不断增加。对于各APP而言,接入IM SDK(即时通讯软件开发工具包)能够大大降低开发成本、提高开发效率,快速构建自己的IM系统。本文主要介绍了百度APP Android IM SDK的建设背景、IM SDK主要结构和工作流程以及建设过程遇到的问题和解决方案。
01 背景
1.1 IM系统发展背景
近年来,随着互联网的普及和移动通信技术技术的快速发展,智能手机、平板电脑等移动设备的普及让越来越多的人享受到便捷的网络服务。这为即时通讯系统的发展提供了广泛的用户基础。传统的通讯工具如电话、短信等在满足用户需求方面存在一定的局限性,无法实现高效、便捷地沟通。即时通讯系统应运而生,以其强大的功能和便捷的体验满足了用户的便捷、高效通讯的需求。
1.2 IM系统简介
即时通讯系统(Instant Messaging,简称IM系统)是一种允许用户通过互联网实时交换信息的通信技术。核心功能包括消息的发送与接收、用户状态的管理、消息、会话的存储与检索等。为了更好地满足用户更多场景诉求,IM系统还提供了如群组聊天、文件传输、语音和视频通话等功能。
1.3 IM系统应用
即时通讯系统的发展经历了从早期的在线聊天室、ICQ、MSN等个人即时通讯软件,到如今功能丰富的聊天社交软件(如QQ、微信)、企业级即时通讯系统(如钉钉、企业微信、飞书、如流)、应用内必备的IM能力(如百度、微博、小红书、抖音等APP中消息模块)等应用场景。IM系统因其实时性、便捷性和功能多样性,已经成为现代社会沟通不可或缺的工具,并且随着技术的发展,其应用场景还在不断扩展。
02 百度公共IM系统
2.1 百度公共IM系统背景
在百度公司内部,存在如百度APP、文心一言APP、百度贴吧APP、好看视频APP等各类APP,各业务各自搭建一套完整的IM系统存在开发、维护成本高,系统重复等问题。因此,搭建一套公共的能够满足各产品线诉求的完整IM系统势在必行。各业务只需独立接入公共IM系统即可实现业务自己的IM相关功能,大大降低了开发、维护成本和IM系统使用门槛,提高了业务IM功能开发、迭代效率。
2.2 百度公共IM系统结构
对于公司内业务而言,对于IM能力诉求各有差异。有的业务希望最小成本构建自己的IM服务,希望提供一套从消息收发到上层聊天交互的完整服务;有的业务希望使用自己的UI套件;有的业务只想使用粉丝群能力;也有业务有自己的UI和业务逻辑,只想使用基础、实时、稳定的消息通道。为了满足各业务/产品线各类诉求,需要将IM系统能力进行拆分、封装,提供不同能力粒度的IM组件。
百度公共IM系统由客户端IM 套件(IM SDK、长连接SDK、IM插件、群聊组件)和服务端IM服务组成。公共IM系统服务各业务方案结构如下:
基础服务层主要由公共IM系统中客户端IM套件、服务端IM服务,以及业务自己服务端组成。业务客户端通过接入IM SDK、长连接SDK实现业务客户端内IM相关能力,业务服务端通过接入公共IM系统中的服务端对外接口层,实现业务服务端消息发送和接收。通过业务客户端和服务单接入IM客户端套件和服务端,实现从业务端到业务服务端的双向触达。其中:
1. 业务服务端:
业务自己的服务端主要负责业务相关的业务逻辑处理,通过调用IM 服务端提供的公共接口进行消息发送,接收IM 服务端推送的消息,进行业务自有逻辑处理,根据业务自身情况决定是否自己持有数据。
2. IM 服务端:
提供实时、安全、稳定的登录服务、消息收发,消息会话同步、通知,消息、会话管理,用户信息管理、设置管理等服务。提供各服务对端接口,暴露对业务公共接口,方便业务快速接入。
3. 长连接SDK主要职责:
长连接SDK主要通过约定数据协议、数据加解密、访问控制、数据压缩、创建、维护连接,处理异常等机制保证数据实时、安全、高效传输。
4. IM SDK主要职责:
IM SDK主要负责IM相关端业务逻辑处理,包括登录服务、消息、会话管理、未读数管理、用户、设置管理等能力。用户使用手机进行消息发送,使用IM 服务端对端接口协议,通过长连接SDK数据通道将消息发送到IM 服务端;通过长连接通道接收IM 服务端推送消息,经过业务逻辑处理后,触达到用户。
5. IM插件主要职责:
通过长连接SDK构建消息通道,使用IM SDK提供的IM逻辑服务层,增加聊天页、设置页等相关UI能力,打包构建出能够直接集成使用的聊天组件。
6. 群聊组件主要职责:
通过长连接SDK构建消息通道,使用IM SDK提供的IM逻辑服务层,将群聊相关UI页面组合打包,输出包含完整群聊能力可直接集成的群聊组件。
03 百度Android端IM SDK组件设计与实现
3.1 整体架构
Android IM SDK整体由公共接口层、业务层和数据层组成,其中:
接口层:根据业务需要,对外统一提供消息、会话、设置项等相关操作接口以及未读数、成员等相关查询接口
业务层:在原始数据基础上增加业务相关逻辑,通过接口层返回给SDK使用方,包括但不限于以下主要模块:
登录管理:负责在IM SDK初始化后长连接建连、重连,百度账号体系登录、退登、重登等情况下,IM 系统的登录、退登,登录信息、事件管理以及异常处理机制等;
同步管理:负责在IM登录后同步单聊、群聊会话,消息、通知消息等账号内相关数据;
配置管理:登录后负责管理用户在IM系统中相关全部配置项;
通知管理:负责用户处于在线/离线状态时系统通知处理,包括但不限于通知监听、解析、指令调度分发等操作;
消息管理:基于原始消息数据,提供消息发送、删除、更新、撤回、查询等操作;
会话管理:负责基础会话数据管理以及会话创建、更新、删除、查询以及会话监听管理、变更分发等机制;
群成员/好友管理:管理登录后会话中的一些联系人数据以及群成员信息;
群管理:负责管理群消息、群操作(如拉人、 踢人、退群、解散、禁言、增加管理员等操作);
数据层:数据层主要包含业务产生的原始数据的存储以及对原始数据的最原始操作;
3.2 核心流程
△登录同步主要流程
△消息上下行主要流程
在IM SDK整个工作流程中,核心流程主要包括登录管理(初始化、长连接连接管理、IM登录、退登等)、数据同步(消息、会话用户信息等数据同步)、通知管理(离线、在线通知处理)、消息上下行等核心流程。IM SDK组件要在保证消息安全、可靠、实时触达到用户的同时,还要避免消息丢失、重复、用户离线状态消息丢失、在线或离线状态多端数据不一致、以及未读数不一致等问题。以下是关于IM SDK核心流程详细介绍:
3.2.1 登录管理
IM系统登录成功是使用IM服务的前提,准备使用IM服务时,要对IM SDK依赖的基础环境、功能能力进行初始化配置,达到环境可用后才能登录系统,IM系统登录成功后,才能完整使用IM SDK提供能力。
挑战:依赖的运行环境变更时,如何快速恢复
问题描述:
完整的IM工作环境需要有正常的网络,百度账号登录且状态正常,长连接稳定可用等,如果其中一个必要依赖异常时,IM系统便处于异常状态,无法继续完整使用IM SDK能力,出现SDK接口调用结果不符合预期的情况。比如网络断开或不可用时,长连接处于断连状态,对IM SDK如何感知?网络再次可用时,IM 服务如何恢复?IM服务不可用时,无法接收到新消息,IM服务恢复后,如何系统性地恢复?
问题解决:异常恢复机制
此类问题发生后,为了避免IM服务长期不可使用,需要增加异常恢复机制,使得某些环境条件恢复正常时,IM系统服务能够快速恢复到最新状态。主要工作包含:
异常捕捉:增加网络环境、账号登录状态、长连接连接状态等环境监听感知;
异常反馈:捕获异常后,需要在调用SDK接口时将用户相关环境异常信息返回,反馈给调用方;
服务快速恢复:感知依赖环境恢复后,自动对流程中依赖重新配置,重新登录IM服务,恢复服务不可用期间用户数据;
其中其核心流程如下:
正常流程下,百度账号登录后初始化IM SDK,IM SDK针对账号、长连接等增加状态监听(还包含IM相关消息、会话、用户信息等变更等业务相关监听),登录长连接会登录IM系统,IM系统登录成功后同步用户消息数据。
长连接状态异常:长连接由于客户端网络或服务异常导致连接断开时,根据IM SDK服务不可用,长连接重新连接后重新登录IM系统,登录成功后增量同步用户服务不可用之后的数据;
账号状态异常:当用户退出账号登录时,会先退出原账号登录,切换为游客用户登录;当用户切换为其他账号时,会先退出原账号登录,切换为新账号登录;增量同步用户服务账号时刻之后的数据;
通过以上流程,可以很好地避免因为网络、长连接状态、账号状态变更时,IM服务能够快速重试,重置登录配置,重新登录IM,快速恢复用户数据,保持状态异常前后,消息及时触达。
3.2.2 数据同步
IM登录成功后,需要通过长连接SDK从服务端全量或增量同步当前用户离线阶段产生的单聊、群聊会话、消息、联系人/群成员信息等数据,更新未读数、会话中lastMsg等操作,保持用户本地和服务端远程数据一致。
挑战一:如何提升客户端和服务端性能的
问题描述:
IM SDK在账号登录后,会登录IM系统,由于账号登录状态时效较长,绝大部分情况下账号均处于登录状态,每次启动APP时都会请求IM登录,IM登录后再同步用户会话和消息。百度APP拥有上亿用户,在用户集中使用APP的时间区间内,qps量级非常大,在服务端资源有限的情况下,大qps量级对IM服务端是一个不小的挑战。为了保持服务的稳定性,在资源有限的情况下,需要在业务逻辑上对请求做优化,降低服务qps,保持服务稳定可用。
问题解决:会话拉取versionCode机制+消息拉取队列
针对以上背景,优化的核心主要是在业务逻辑上减少IM登录到IM同步期间的服务端请求,主要优化点如下:
客户端登录后减少非必须请求,总未读数获取由从服务端获取,改为先使用客户端本地计算,同步数据后再更新;
客户端每次同步会话时,将所有新会话放入同步队列中,防止每个会话开一个线程,增加客户端线程池饱和风险,减少服务端并发请求。
服务端对会话同步请求增加versionCode机制:每次客户端请求获取会话后,服务端返回一个versionCode(如果后续有新会话,才会更新versionCode),客户端下次登录再请求拉会话时如果传参的versionCode和服务端最新的versionCode一致,表示没有新会话,不需要查库等操作,直接返回空结果;
主要流程如下:
挑战二:如何避免同步失败时消息丢失问题
问题描述:
为了降低服务端qps,在拉取会话时增加了versionCode机制,如果第一次拉取会话后没有新会话产生,后续拉会话时服务端根据versionCode判断服务端和客户端传参versionCode一致,表示端上会话列表和服务端一致,直接返回结果,表示没有新会话返回。如果在拉取完会话后,每条会话消息还未拉去完毕,此时断网或长连接中断,导致部分会话的消息没有拉取或没有拉取完毕,状态恢复再次重试拉取时由于已经获取到了最新的versionCode,再次从server拉取会话时无法拉取到会话,导致部分会话消息丢失。
如果拉取某条会话的消息时,拉取请求服务异常,如果抛弃当前任务执行之后的任务,依然请求异常,导致队列中后续部分或剩余所有任务请求失败,消息拉取失败,导致无法拉取到部分会话消息,导致消息丢失;
问题解决:versionCode延迟记录+重试机制
根据以上问题描述,对于问题1,需要保持versionCode的有效性,对于问题一发生时,客户端当次获取的versionCode其实已经是失效状态,需要在会话、消息都拉取完毕时记录versionCode,保持客户端本地和服务端均有效。
对于问题2,队列中的任务要根据具体异常执行跳过策略,如果是因为服务端内部错误导致的同步失败,可以跳过,对于网络或长连接状态异常,可以增加重试机制,超过重试次数才停止任务,从而增加消息拉取成功率。
主要流程如下:
3.2.3 通知管理
通知下行
用户在线阶段,如过有新消息或者消息已读、删除、会话删除、置顶、免打扰状态变更等多端同步情况时,服务端会下行对应通知消息,通知当前登录设备处理新操作。
以新消息通知为例,如果有其他用户给当前用户发送消息,消息到达服务端后,服务端根据用户在线状态,通过长连接通道下发新消息通知;端根据约定解析对应通知消息,识别新消息通知,开始拉取新消息操作,拉取新消息后更新会话lastMsg、未读数相关信息,转发变更到业务层,更新UI交互。
挑战一:如何实现同一账号在线设备操作后,其他离线设备在线时用户数据一致性
问题概述:
如果同一用户有多台手机,用户部分设备处于离线状态(设备断网或未打开APP),如果用户使用在线状态的手机执行了已读会话、删除会话、删除消息等操作,操作完毕后,再打开离线的设备,使其保持在线状态,此时刚保持在线状态的设备会话、消息、未读数状态仍未改变,出现两台设备消息/会话等状态不一致问题。
以已读操作为例:
如果当前用户两台设备(设备A和设备B)都收到了用户小明发来的5条消息,设备B断网或APP进程关闭。用户使用一台设备A已读了和用户小明的聊天信息,设备A中和用户小明的聊天会话中未读数变为0;打开设备B,使其处于在线状态,设备B和用户小明的会话仍显示有5条未读数。
问题解决:指令机制
对于此类用户多设备的在线状态不一致的情况,操作APP内消息或会话后,出现用户多台设备在线时设备数据不一致问题,问题根源在于设备B重新在线后无法感知到设备A的操作,如果设备B重新在线后能获取到设备A的操作,并执行设备A的操作,就能保持两台(或多台)设备数据一致。
需要把用户操作数据化,将用户操作构造为一条“指令”消息保存到服务端,等设备再次在线后,拉取到离线期间未接收到的消息后,拉取设备离线期间的操作指令消息,解析指令消息后,执行对应的操作。
以设备在线后消息已读指令消息为例,相关执行流程如下:
挑战二:如何实现同一账号多台在线设备数据一致性
问题概述:
IM系统中,数据一致性是指在多设备环境中,用户如果有多台设备或终端,各个设备登录同一账号,各个设备下显示的用户消息数量、消息未读状态、会话未读数、会话最近一条消息等要保持一致。实际生活场景下,一个用户存在多台设备,可能存在如下情case:
case1:设备全部处于在线状态
如果当前用户使用一台设备(例如设备A)向用户“小明”发送一条内容为“你好啊”的消息,当前用户的另一台设备(例如设备B)也需要在和用户小明的聊天也中显示自己发送了一条内容为“你好啊”的消息内容。
问题解决:多端同步机制
对于同一账号登录多个设备的情况,设备均在线时,如果其中一台设备发送一条消息(或者进行已读、删除消息、删除会话、修改会话置顶、免打扰状态等操作),服务端会将新发送的消息通知推送到登录同一账号的其他设备上,其他设备接收到新消息通知后,再去拉取当前账号在其他设备发送的消息,更新会话,从而达到登录同一账号的多台设备数据同步,保持数据一致性。
每个操作对应于一条通知消息,登录后同步当前设备离线期间产生的通知消息后,根据通知消息里携带的操作信息,再次执行对应的操作,实现多端同步效果。
3.2.4 消息收发
挑战一:以什么样的方式获取新消息
问题描述:
当有其他用户给当前设备发消息时,要实时展现其他用户发送的消息,就要及时地获取到对应消息。大概存在几种方案:
解决方案:推拉结合
综合以上方案,保证服务稳定性是系统的关键,想要消息不丢失,并且能稳定触达的同时还要保证客户端和服务端系统的稳定性,推拉结合的方案更适合。
方案描述:用户发送新消息时,服务端拣选新消息关键信息字段,构造一条通知消息推送给接收人。接收人收到通知消息后,解析通知消息内容,理解对用通知操作后,从服务端拉取新消息。
挑战二:如何保证消息可靠投递
问题描述:
系统需要保证消息的可靠传输,不会丢失或重复,确保消息的顺序和完整性
IM系统的消息“可靠性”,通常就是指聊天消息可靠投递。即对于消息发送方发出消息,要保证消息接收方及时、顺序接收到,并在UI中正常展示(不丢失、不重复)。
在现实的使用场景中,从发送方点击「发送」按钮到接收方接收到消息,发送接收消息的整条链路中包含:消息协议组装、长连接通道发消息、服务端接收到消息,服务端保存消息、服务端通过长连接下行通知消息,接收方根据通知拉取消息。
如果发送消息时接收方处理离线状态,或者发送消息时长连接因为网络或异常中断、服务端服务异常、消息下行时长连接异常等情况下,链路中任意一环异常导致链路中断均会导致消息无法到达接收方,即消息丢失。大致分为:消息上行阶段消息丢失、消息下行阶段消息丢失。
解决方案:失败重试与重连拉取机制
想要保证实时&离线消息的可靠投递,需要对收发消息的整条链路各阶段增加从产品交互到技术方案上增加兜底和容错处理。
消息上行服务异常处理增加失败重试机制:
IM SDK发送上行请求,长连接因为网络或其他原因导致长连接服务不可用、服务端服务异常时,消息发送失败,需要对发送失败的消息做标记,UI上提供视觉展示,增加重新发送机制,在交互上避免用户发消息失败时出现消息已发送对方收不到的错误预期,提高服务恢复时功能可用性。
消息下行重新拉取机制流程如下:
对于服务端推送到客户端的消息,服务端需要将消息存储,如果用户处于在线状态,则推送新消息通知给接收用户;
如果服务端推送下行通知消息时,接收方长连接服务处于不可用/不稳定的情况,端需要增加长连接连接状态监测,重连拉取机制;监测到长连接重新连接后,主动重新拉取服务不可用时刻之后的消息,保障用户能够及时看到离线期间漏掉的消息;
04 总结
根据各类APP对于IM系统不同构建方案诉求,公共IM系统提供了包含IM各类能力粒度、高内聚、可扩展的长连接 SDK、IM SDK、IM聊天插件、群聊组件;提供了中台NA容器、HN容器、公共Web容器下IM聊天页接入方案;依托IM 服务端,提供了一套实时、可靠、稳定的IM服务。满足了各业务方快速、高效构建各自产品线IM能力的愿望,大大提高了开发效率、降低了开发成本。
实时、可靠、安全是对IM系统的基础要求,系统地收发消息机制提供了最小粒度的IM服务,实时通知、离线获取能够更好地保障IM功能完整性,实时、离线多端同步能力提升了同一账号多台设备的产品体验。以功能、交互体验需求为出发点而进行方案设计的同时也要考虑产品性能以及端、服务端、业务的实现成本。
随着用户需求和市场变化,我们需要不断迭代优化,升级系统功能和性能,更细致、高效地满足业务诉求,为用户提供更加优质、高效的即时通讯服务。