从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

一、引言

移动互联网技术改变了旅游的世界,这个领域过去沉重的信息分销成本被大大降低。用户与服务供应商之间、用户与用户之间的沟通路径逐渐打通,沟通的场景也在不断扩展。这促使所有的移动应用开发者都要从用户视角出发,更好地满足用户需求。

论坛时代的马蜂窝,用户之间的沟通形式比较单一,主要为单纯的回帖回复等。为了以较小的成本快速满足用户需求,当时采用的是非实时性消息的方案来实现用户之间的消息传递。

随着行业和公司的发展,马蜂窝确立了「内容+交易」的独特商业模式。在用户规模不断增长及业务形态发生变化的背景下,为用户和商家提供稳定可靠的售前和售后技术支持,成为电商移动业务线的当务之急。

本文由马蜂窝电商业务 IM 移动端研发团队分享了马蜂窝电商业务 IM 移动端的架构演进过程,以及在IM技术力量和资源有限的情况下所踩过的坑等。

系列文章:

  • 《从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路》
  • 《从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结》(* 本文)

关于马蜂窝旅游网:

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

为了实现马蜂窝旅游 App 及商家 IM 业务逻辑、公共资源的整合复用及 UI 个性化定制,将问题拆解为以下部分来解决:

1)IM 数据通道与异常重连机制:解决不同业务实时消息下发以及稳定性保障;

2)IM 实时消息订阅分发机制:解决消息定向发送、业务订阅消费,避免不必要的请求资源浪费;

3)IM 会话列表 UI 绘制通用解决方案:解决不同消息类型的快速迭代开发和管理复杂问题。

整体实现结构分为 4 个部分进行封装,分别为下图中的数据管理、消息注册分发管理、通用 UI 封装及业务管理。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

常见的客户端与服务端数据交互依赖于 HTTP 请求响应过程,只有客户端主动发起请求才可以得到响应结果。结合马蜂窝的具体业务场景,我们希望建立一种可靠的消息通道来保障服务端主动通知客户端,实现业务数据的传递。目前采用的是 HTTP 长链接轮询的形式实现,各业务数据消息类型只需遵循约定的通用数据结构,即可实现通过数据通道下发给客户端。数据通道不必关心数据的具体内容,只需要关注接收与发送。

3.1.2 客户端数据通道实现原理

客户端数据通道管理的核心是维护一个业务场景请求栈,在不同业务场景切换过程中入栈不同的业务场景参数数据。每次 HTTP 长链接请求使用栈顶请求数据,可以模拟在特定业务场景 (如与不同的用户私信) 的不同处理。数据相关处理都集中封装在数据通道管理中,业务层只需在数据通道管理中注册对应的接收处理即可得到需要的业务消息数据。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

业务层订阅需要处理的业务消息类型,在注册后会自动监控当前页面的生命周期,并在页面销毁后删除对应的消息订阅,从而避免手动编写成对的订阅和取消订阅,降低业务层的耦合,简化调用逻辑。订阅分发管理会根据各业务类型维护订阅者队列用于消息接收的分发操作。

3.2.2 消息分发

数据通道的核心在于维护多消息类型各自对应的订阅者集合,并将解析的消息分发到业务层。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

3.3.2 消息类型与展示布局管理原理

对于不同消息类型及展示,问题的核心在于建立消息类型、消息数据结构、消息展示布局管理的映射关系。以上三者在实现过程中通过建立映射管理表来维护,各自建立列表存储消息类型/消息体封装结构/消息展示布局管理,设置对应关系关联 3 个列表来完成查找。 

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

拆分的目的在于使各类型消息 UI 处理只需要关注特有数据。而如通用消息如头像、名称、消息时间、是否可举报、已读未读状态、发送失败/重试状态等都可以统一处理,降低修改维护的成本,同时使各消息 UI 处理逻辑更少、更清晰,更利于新类型的扩展管理。

收发到消息后,根据消息类型判断是「发送接收类型」还是「居中展示类型」,找到外层的布局样式,再根据具体消息类型找到特有的 UI 样式,拼接在外层布局中,得到完整的消息卡片,然后设置对应的数据渲染到列表中,完成整个消息的绘制。

四、细节优化 & 踩坑经验

在实现上述 IM 系统的过程中,我们遇到了很多问题,也做了很多细节优化。在这里总结实现时需要考虑的几点,以供大家借鉴。

4.1、消息去重

在前面的架构中,我们使用 msg_id 来标记消息列表中的每一条消息,msg_id 是根据客户端上传的数据,进行存储后生成的。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

当客户端 A 因为网络出现问题,无法接受对应发送消息的请求返回的时候,会触发重发机制。此时虽然 IM 服务器已经接受过一次客户端 A 的消息发送请求,但是因为无法确定两个请求是否来自同一条原始消息,只能再次接受,这就导致了重复消息的产生。解决的方法是引入客户端消息标识 id。因为我们已经依附旧有的 msg_id 做了很多工作,不打算让客户端的消息 id 代替 msg_id 的职能,因此重新定义一个 random_id。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

通过这种数据通道+本地通知展示的机制,可以在应用处于运行状态的时间内提高消息抵达率,减少对于远程推送的依赖,降低推送系统的压力,并提升用户体验。

4.3、数据通道异常重连机制

当前数据通道通过 HTTP 长链接轮询 (Polling) 实现。

不同业务场景下对 Polling 的影响如下图所示:

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

在实践中发现以下问题:

1)当服务端突然异常并持续超过 1 分钟后,客户端启动执行重试机制,并每隔 1 分钟重发一次重连请求。这对服务器而言就相当于遭受一次短暂集中的「攻击」,甚至有可能拖垮服务器;

2)当客户端断网后立刻进行重试也并不合理,因为用户恢复网络也需要一定时间,这期间的重连请求是无意义的。

基于以上问题分析改进,我们设计了第二版重试机制。此次将 5 次以下请求错误的延迟时间修改为 5 – 20 秒随机重试,将客户端重试请求分散在多个时间点避免同时请求形成对服务器对瞬时压力。同时在客户端断网情况下也进行延迟重试。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

4.4、唯一会话标识

4.4.1 为何引入消息线 ID

消息线就是用来表示会话的聊天关系,不同消息线代表不同对象的会话,从 DB 层面来看需要一个张表来存储这种关系 uid + object_id + busi_type = 消息线 ID。

 

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

近期将对 HTTP 轮询实现方案进行替换,进一步优化数据通道的效率。

5.2、业务功能的扩展

计划将 IM 移动端功能模块打造成通用的即时通讯组件,能够更容易地赋予各业务 IM 能力,使各业务快速在自有产品线上添加聊天功能,降低研发 IM 的成本和难度。目前的 IM 功能实现主要有两个组成,分别是公用的数据通道与 UI 组件。

随着马蜂窝业务发展,在现有 IM 系统上还有很多可以建设和升级的方向。比如消息类型的支撑上,扩展对短视频、语音消息、快捷消息回复等支撑,提高社交的便捷性和趣味性;对于多人场景希望增加群组,兴趣频道,多人音视频通信等场景的支撑等。

相信未来通过对更多业务功能的扩展及应用场景的探索,马蜂窝移动端 IM 将更好地提升用户体验,并持续为商家赋能。

附录:更多IM架构设计方面的文章

《浅谈IM系统的架构设计》
《简述移动端IM开发的那些坑:架构设计、通信协议和客户端》
《一套海量在线用户的移动端IM架构设计实践分享(含详细图文)》
《一套原创分布式即时通讯(IM)系统理论架构方案》
《从零到卓越:京东客服即时通讯系统的技术架构演进历程》
《蘑菇街即时通讯/IM服务器开发之架构选择》
《腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT》
《微信后台基于时间序的海量数据冷热分级架构设计实践》
《微信技术总监谈架构:微信之道——大道至简(演讲全文)》
《如何解读《微信技术总监谈架构:微信之道——大道至简》》
《快速裂变:见证微信强大后台架构从0到1的演进历程(一)》
《17年的实践:腾讯海量产品的技术方法论》
《移动端IM中大规模群消息的推送如何保证效率、实时性
《现代IM系统中聊天消息的同步和存储方案探讨》
《IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构
《IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议》
《IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token》
《WhatsApp技术实践分享:32人工程团队创造的技术神话》
《微信朋友圈千亿访问量背后的技术挑战和实践总结》
《王者荣耀2亿用户量的背后:产品定位、技术架构、网络方案等》
《IM系统的MQ消息中间件选型:Kafka还是RabbitMQ
《腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面》
《以微博类应用场景为例,总结海量社交系统的架构设计步骤》
《快速理解高性能HTTP服务端的负载均衡技术原理》
《子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践》
《知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路》
《IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列》
《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》
《微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)》
《新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践》
《一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践》
《阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史》
《阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路》
《社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等》
《社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进》
《社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节》
《社交软件红包技术解密(四):微信红包系统是如何应对高并发的》
《社交软件红包技术解密(五):微信红包系统是如何实现高可用性的》
《社交软件红包技术解密(六):微信红包系统的存储层架构演进实践》
《社交软件红包技术解密(七):支付宝红包的海量高并发技术实践》
《社交软件红包技术解密(八):全面解密微博红包技术方案》
《社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等》
《即时通讯新手入门:一文读懂什么是Nginx能否实现IM的负载均衡
《即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途》
《多维度对比5款主流分布式MQ消息队列,妈妈再也不担心我的技术选型了》
《从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路》
《从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结》
《IM开发基础知识补课(六):数据库用NoSQL还是SQL这篇就够了!》
>> 更多同类文章 ……
 

(本文同步发布于:http://www.52im.net/thread-2796-1-1.html)

来源:hellojackjiang2011

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2019年9月15日
下一篇 2019年9月15日

相关推荐