多点登录与消息漫游怎么实现?(5/31)

cache 多 key、投递给发送方的其他端、云端存储与 last_msg_id

即时通讯架构实践第 5 课封面:即时通讯05:多点登录与消息漫游怎么实现

cache 多 key、投递给发送方的其他端、云端存储与 last_msg_id

《即时通讯架构核心技术》第 5 篇:多点登录与消息漫游怎么实现。

有朋友问我说:

1. 微信如何实现手机端、PC端同时登录,同时收消息?

2. 微信能不能实现,换一个手机,仍能拉取到历史消息?

这是多点登录和消息漫游的问题,结合自己的经验,聊聊相关功能的架构实现。

什么是多点登录?

以微信为例,可以PC端,手机端同时登录,同时收发消息。

需要注意的是,一个端只能登录一个实例,例如同一个QQ号,在pc1上登录,再到pc2上登录,后者会把前者踢出,pc1会收到通知”你已在别处登录xxoo”。

什么是消息漫游?

在任何一个终端的任何一个实例登录,都能够拉取到所有历史聊天消息,这个就是消息漫游。

微信目前只支持”多点登录”同时收发在线消息,以及最近几条消息的”消息漫游”,没有实现全部消息的”漫游”。

典型即时通讯架构如何?

典型即时通讯架构可以抽象成这么几层:

1. 客户端:例如pc微信,手机qq

2. 服务端:

入口层gate:保持与客户端的连接;

逻辑层logic、路由层router:实现业务逻辑,进行消息的路由;

cache:用来存储用户的在线状态,与接入节点(用户具体连接在哪个gate节点);

db:固化存储消息,群信息,好友关系链等信息;

都需要考虑高可用,扩展性。

典型即时通讯架构与消息投递流程

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

(1) 用户A登录在gate1上,发出消息;

(2) gate1将消息给logic/router;

(3) logic/router查询接收方的在线状态(B在线,C不在线);

(4.1) 假设接收方C不在线,存储离线;

(4.2) 假设接收方B在线,且登录在gate2上,消息投递给gate2;

(5) gate2将消息投递给B;

单对单消息有一系列应用层超时、重传、确认、去重的机制,以保证消息的可靠投递,这不是本文的重点,不进行展开。

实现”接收方”多点登录,架构要做什么调整?

接收方多点登录的架构调整

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

单点登录

B:online(状态),gate2(登录点)

多点登录

B+pc:online(状态),gate2(登录点)

B+phone:online(状态),gate3(登录点)

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

接收方多点登录,比较容易。

实现”发送方”多点登录,架构要做什么调整?

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

假设:

用户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端的发出)。

这个逻辑有点绕,多看几遍。

故,如果发送方也进行了多点登录,发送出去的任何消息,除了要投递给多点登录的接收方,还需要投递给多点登录的发送方。

发送方发出的消息,也需要发给”发送方”。

发送方与接收方都多点登录的投递流程

如上图,发送方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。

只有这样,才能在所有用户的所有端,恢复与还原双方聊天的上下文。

搞清楚了原理,修改并不难。

实现”消息漫游”,架构要做什么调整?

如果不需要支持”消息漫游”,对于在线消息,如果用户接收到,是不需要存储到数据库的。但如果要支持”换一台机器也能看到历史的聊天消息”,就需要对所有消息进行存储。

支持消息漫游的投递流程:投递前先存储

消息投递如上图,用户A发送消息给用户B,虽然B在线,仍然要增加一个步骤2.5,在投递之前进行存储,以备B的其他端登录时,可以拉取到历史消息。

消息漫游的历史消息拉取流程

消息拉取如上图,原本不在线的B(phone端)重新登录了,怎么拉取历史消息呢?

只需要在客户端本地存储一个上一次拉取到的msg_id(time),到服务端重新拉取即可。

这里还有个问题,由于服务端存储所有消息成本是非常高的,所以一般”消息漫游”是有时间(或者消息数)限制,不能拉取所有几年前的历史消息,通常只拉取最近的云端消息。

你猜,微信有没有存储,几年前的消息呢?你猜,交一个超级会员费,是不是可以查询呢?

稍作总结

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

1. 需要在服务端存储同一个用户多个端的状态与登录点;

2. 发出消息时,要对发送方的多端与接收端的多端,都进行消息投递;

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

1. 所有消息存储在云端;

2. 每个端本地存储last_msg_id,在登录时可以到云端同步历史消息;

3. 云端存储所有消息成本较高,一般会对历史消息时间(或者条数)进行限制;

检验一下

检索练习 · 接收方多点登录
支持接收方多点登录,cache 中状态与登录点的存储要怎么改?

检索练习 · 发送方多点登录
发送方也多点登录时,发出的消息为什么还要投递给发送方自己的其他端?

检索练习 · 消息漫游
实现消息漫游,架构上最关键的调整是什么?

检索练习 · 漫游为什么限量
为什么消息漫游通常只能拉最近的消息,不能拉几年前的全部?

功能层面聊完了,往底层走一步:IM 的应用层、安全层、传输层,协议分别怎么选?——第 6 篇,应用层/安全层/传输层,协议怎么选。

知其然,知其所以然。思路比结论更重要。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

京ICP备2025137728号-3