默认
打赏 发表评论 38
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
浅谈移动端IM的多点登陆和消息漫游原理
阅读(259955) | 评论(38 收藏16 淘帖1 2
微信扫一扫关注!

1、前言


在移动端IM大行其道之前的PC端IM时代,所谓的多端登陆控制无非就是当你在一台电脑上登陆后,再在另一台电脑上登陆时就会把之前登陆的账号给踢出(比如QQ就是这样)。同样PC端时代的IM消息漫游不过就是可以在另一台电脑上下载之前的历史记录而已(参考PC端QQ)。

但自从Google在2013年的开发者大会(Google I/O)上公布了一款叫“Google 环聊”(网址:hangouts.google.com)的移动端IM后,凭借Google的创意,多端登陆和消息漫游才真正有了现在的体验(Google环聊无法体验的话,参考当前版本的微信、手机QQ就可以了):即允许同一账号在多种设备上同时登陆和使用、且消息会以某种逻辑同步到另外的端上(包括自已发出的消息)等特性,使用得移动端IM的消息同步第一次真正的变的友好和易用。下图是Google环聊的主界面。

浅谈移动端IM的多点登陆和消息漫游原理_2013516161516320320.png

本文将展开聊聊移动端IM“多点登陆”与“消息漫游”的原理。

* 推荐阅读:阿里的《深度解密钉钉即时消息服务DTIM的技术设计》一文中的“六、多端同步机制设计”章节也详细分享了钉钉的消息同步机制,可以一并阅读。

2、IM开发干货系列文章


本文是系列文章中的第9篇,总目录如下:


另外,如果您是IM开发初学者,强烈建议首先阅读《新手入门一篇就够:从零开发移动端IM》。

3、基本概念


什么是多点登录?
以微信为例:可以PC端、phone端同时登录、同时收发消息。需要注意的是:一个端只能登录一个实例,例如同一个QQ号,在pc1上登录再到pc2上登录,后者会把前者踢出,pc1会收到通知“你已在别处登录xxoo”。比如在微信上两个手机同时登陆同一个账号时就会踢出前一个登陆并给出如下图所示的提示:

浅谈移动端IM的多点登陆和消息漫游原理_x.jpg

什么是消息漫游?
在任何一个终端的任何一个实例登录qq,都能够拉取到所有历史聊天消息,这个就是消息漫游。微信目前只支持“多点登录”同时收发在线消息(如果你同时开过PC端微信和手机端微信,你可以注意到你收到和发出的消息都会在另一个端同时显示出来),没有实现“消息漫游”,潜台词是:登出手机微信,登录PC微信,聊天,再登录手机微信是看不到历史消息的。

4、一个典型的IM消息收发架构


浅谈移动端IM的多点登陆和消息漫游原理_1.jpg

整个IM的架构可以抽象成这么几层:

  • 1)客户端:例如pc微信,手机qq
  • 2)服务端:
            2.1)入口层gate集群:能够水平扩展,保持与客户端的连接;
            2.2)逻辑层logic、路由层router集群:高可用可扩展,实现业务逻辑,进行消息的路由;
            2.3)cache:高可用cache集群,用来存储用户的在线状态,与接入节点(用户具体连接在哪个gate节点);
            2.4)db:固化存储消息,群信息,好友关系链等信息。

一个典型的消息收发流程如上图步骤1-5:

  • 1)用户A登录在gate1上,发出消息;
  • 2)gate1将消息给logic/router;
  • 3)logic/router查询接收方的在线状态(B在线,C不在线);
  • 4)例如接收方C不在线,存储离线;
  • 4)例如接收方B在线,且登录在gate2上,消息投递给gate2;
  • 5)gate2将消息投递给B。

当然,单对单消息有一系列应用层超时、重传、确认、去重的机制,这不是本文的重点,不进行展开,细节详见《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》、《IM消息送达保证机制实现(二):保证离线消息的可靠投递》。

5、接收方多点登陆时的消息投递原理


浅谈移动端IM的多点登陆和消息漫游原理_2.jpg

接收方多点登录:pc登录、phone也登录,后一端登录不会将前一端踢出,cache中存储状态与登录点时,不再以user_id为key,改为以user_id+终端类型为key即可。

传统的PC端IM时的B客户端信息为:online(状态),gate2(登录点)。

在支持多端登陆的移动端IM时信息应改为:
B的pc客户端:online(状态),gate2(登录点);
B的phone客户端:online(状态),gate3(登录点)。

当用户A给用户B发送消息时,取出所有B的登录点,进行消息群发即可(如上图中步骤4与步骤5)。

6、发送方多点登陆时的消息投递原理


有朋友可能要问,发送方和多点登录有什么关系?

假设用户A登录了两个点:A1和A2;用户B登录了两个点B1和B2:

  • A(A1发出的)发送消息给B(B1和B2);
  • B(B1发出的)发送消息给A(A1和A2)。

不就可以了么?

其实不然:A(A1发出的)发送消息给B(B1和B2),B(B1发出的)发送消息给A(A1和A2)。A2端虽然收到了所有B回复的消息,但消息其实是在A1端发出的,故A2端只知道聊天消息的一半(所有B的回复),缺失了聊天的上下文(所有A1端的发出)。故:如果发送方也进行了多点登录,发送出去的任何消息,除了要投递给多点登录的接收方,还需要投递给多点登录的发送方(说白了就是自已发出的消息,也要发送到自已登陆的其它客户端上)。

浅谈移动端IM的多点登陆和消息漫游原理_3.jpg

如上图所示,发送方A和接收方B都进行了多点登陆,服务端cache中存储的信息为:

  • A的pc端:online(状态),gate0(登录点);
  • A的phone端:online(状态),gate1(登录点);
  • B的pc端:online(状态),gate2(登录点);
  • B的phone端:online(状态),gate3(登录点)。

当用户A(phone端)给用户B发送消息时,除了要投递给B的所有多点登录端,还需要投递给A自已多点登陆的其他端(pc端),如上图中步骤4与步骤5。只有这样,才能在所有用户的所有端,恢复与还原双方聊天的上下文。

7、消息漫游原理


如果业务不需要支持“消息漫游”的功能,对于在线消息,如果用户实时接收到则是不需要存储到数据库的。但如果要支持“换一台机器(指的是用户的客户端)也能看到历史的聊天消息”,就需要对所有消息进行存储了。

浅谈移动端IM的多点登陆和消息漫游原理_4.jpg
消息投递如上图:用户A发送消息给用户B,虽然B在线,仍然要增加一个步骤2.5,在投递之前进行存储,以备B的其他端登陆时,可以拉取到历史消息。

浅谈移动端IM的多点登陆和消息漫游原理_5.jpg
消息拉取如上图:原本不在线的B(phone端),又重新登录了,他怎么拉取历史消息?只需要在客户端本地存储一个上一次拉取到的msg_id(time),到服务端重新拉取即可。这里还有个问题:由于服务端存储所有消息成本是非常高的,所以一般“消息漫游”是有时间(或者消息数)限制,不能拉取所有所有几年前的历史消息,比如只能拉取3个月内的云端消息等。

8、本文小结


“多点登录”是指多个端同时登录一个帐号,同时收发消息,关键点是:

  • 1)需要在服务端存储同一个用户多个端的状态与登陆点;
  • 2)发出消息时,要对发送方的多端与接收端的多端,都进行消息投递。

“消息漫游”是指一个用户在任何端,都可以拉取到历史消息,关键点是:

  • 1)所有消息存储在云端;
  • 2)每个端本地存储last_msg_id,在登录时可以到云端同步历史消息;
  • 3)云端存储所有消息成本较高,一般会对历史消息时间(或者条数)进行限制。

(原文链接:微信多点登录与QQ消息漫游架构随想,内容有修订和改动)

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:一个低成本确保IM消息时序的方法探讨下一篇:关于IM离线消息拉取的疑问

本帖已收录至以下技术专辑

推荐方案
评论 38
用户线状态是怎么维护啊,我用了wedsdk 和mobile sdk是两个进程,使用中间件redis维护状态可以么
可以指导下表结构设计下
引用:JackJiang 发表于 2022-01-12 12:27
别纠结了,底线是,你只要保证在某个端有这个消息(表示这消息没有丢),那基本上逻辑就不会有太大问题, ...

谢谢指导
引用:crazyerror 发表于 2022-01-12 11:51
感觉这种实现不太好,如果本地存储每次登录 进行拉取到最新的消息A1结束  , 下次登录的时候 还从本地A1  ...

别纠结了,底线是,你只要保证在某个端有这个消息(表示这消息没有丢),那基本上逻辑就不会有太大问题,谁让他用户没事老换什么手机,换来换去,微信也不会这么给他同步啊
引用:JackJiang 发表于 2022-01-07 18:52
切换设备这种情况,只要保证消息不丢(即一台设备上有)就行了,微信就是这样的,没必要搞那么复杂的逻辑 ...

感觉这种实现不太好,如果本地存储每次登录 进行拉取到最新的消息A1结束  , 下次登录的时候 还从本地A1 继续拉取, 每个消息通过在线推送和每次登录时候拉取, 相当与每个消息 客户端收到两次,保证每个客户端消息可以获取到。但是又感觉这个方案不是太好。
引用:crazyerror 发表于 2022-01-07 21:16
谢谢耐心指导,貌似也是的已经看过了。
微信电脑端,在登录的时候有一个选项,让选择是否同步最近消息 ...

对的
引用:JackJiang 发表于 2022-01-07 18:52
切换设备这种情况,只要保证消息不丢(即一台设备上有)就行了,微信就是这样的,没必要搞那么复杂的逻辑 ...

谢谢耐心指导,貌似也是的已经看过了。
微信电脑端,在登录的时候有一个选项,让选择是否同步最近消息。
引用:crazyerror 发表于 2022-01-07 18:33
请教一下这么对吗?如果一个新的设备,第一次 传递的是一个null,则啦去所有未ack的消息,进行分页啦去, ...

切换设备这种情况,只要保证消息不丢(即一台设备上有)就行了,微信就是这样的,没必要搞那么复杂的逻辑再去处理切换后的设备上的未读消同步问题(在用户看来,你正这样做了,他可能认为你程序有问题,明明那台已经看到拉过了,怎么这台还拉过来)。反正消息已经给你拉到了,谁你用户傻XX的换设备之前不把未读消息看一下,活该
引用:JackJiang 发表于 2022-01-07 18:12
拉取的时候肯定是按顺序、按页拉,每页成工拉取后,最后一条消息的id可以作为一个确认标记(前提是你消息 ...

请教一下这么对吗?如果一个新的设备,第一次 传递的是一个null,则啦去所有未ack的消息,进行分页啦去,每次记录最后一次ack,  拉取完毕后,下一次 登录的时候 按照上一次拉取最后一次ack 消息开始继续拉取 未收到的消息。 如果两个android设备 切换登录是否存在 100-200 消息在 A 拉取过了 200-300 B 拉取过了。 A 再次登录的时候拉取的是300-N 这时候 200-300 就不见了,每次拉取离线消息是 全量拉取  还是  只 拉取 未ACK 的消息?
引用:crazyerror 发表于 2022-01-07 17:45
那么如何确定一个同步点,还是最近几个月没有ack 确认的消息都拉去然后去重呢?

拉取的时候肯定是按顺序、按页拉,每页成工拉取后,最后一条消息的id可以作为一个确认标记(前提是你消息id是有序的)
引用:JackJiang 发表于 2022-01-07 11:42
已同步的可以有个确认动作,没有确认的下次还会拉过来,如果跟实时收到的消息有重复,去重就好了

那么如何确定一个同步点,还是最近几个月没有ack 确认的消息都拉去然后去重呢?
引用:crazyerror 发表于 2022-01-06 23:26
请教一下 假设客户端A 存在离线消息100条 当登录的时候同步了50条信息,又收到1条新的消息,这时候下线了 ...

已同步的可以有个确认动作,没有确认的下次还会拉过来,如果跟实时收到的消息有重复,去重就好了
引用:JackJiang 发表于 2018-09-28 18:45
1、2两点基本上理解是对的。

3、不需要区分什么离线还是多端,反正大于本地消息id或时间戳的就拉下来 ...

请教一下 假设客户端A 存在离线消息100条 当登录的时候同步了50条信息,又收到1条新的消息,这时候下线了,导致重新登录,如果用最新的ID 是否又问题,这时候如何设计同步离线消息?
如果是基于时间戳,集群下如果出现时钟回拨问题,更新就不一定正确了。
签名: hao hao hao hao hao hao hao
引用:小张 发表于 2021-08-27 16:10
是不是没多少个IM应用,有做漫游搜索的?很多都是支持本地?

一般都是本地搜索啦
引用:JackJiang 发表于 2021-08-27 16:00
这种情况,就直接用“加载更多”这个概念就好了,不需要准确的多少页

是不是没多少个IM应用,有做漫游搜索的?很多都是支持本地?
引用:JackJiang 发表于 2021-08-27 16:00
这种情况,就直接用“加载更多”这个概念就好了,不需要准确的多少页

以搜索出来的那条记录作为一页最上一条,然后,在下面就是< > 这种翻页形式~
引用:小张 发表于 2021-08-27 15:47
就像图片中的。用微信举例,条件搜索后,会到一个记录列表,点击“查看上下文”,就会跳转到一个可以拉动 ...

这种情况,就直接用“加载更多”这个概念就好了,不需要准确的多少页
引用:JackJiang 发表于 2021-08-26 15:47
“侧边滚动条”你指的是什么样的东西?能截个图我理解一下吗

就像图片中的。用微信举例,条件搜索后,会到一个记录列表,点击“查看上下文”,就会跳转到一个可以拉动的页面了。如果到了这个页面,但是终端是没有数据的,那不就要往服务端拉很多数据?否则也不知知道这个会话有多少数据量,ui的滚动条大小不知道有多少或者说不知道应该定位到哪个位置。
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部