服务器im协议设计,IM服务器设计-基础

IM做为非常经典的服务器系统,其设计时候的考量具备代表性,所以这一次花几个篇幅讨论其相关设计。

主要内容相当部分参考了 一套海量在线用户的移动端IM架构设计实践分享一文,在此之上补充了更好的消息存储设计以及集群设计。

整体架构

7bcd15540f400c72d44aa776fde7b759.png

以上架构图中,分为几个部分:

客户端:支持IOS、Android系统。

接入层:负责维护与客户端之间的长连接。

逻辑层:负责IM系统中各逻辑功能的实现。

存储层:存储IM系统相关的数据,主要包括Redis缓存系统(用于保存用户状态及路由数据)、消息数据。

上图中几部分的交互如下:

客户端通过gate接入IM服务器。在这里,客户端与gate之间保持TCP长连接,客户端使用DNS查询域名返回最近的gate地址进行连接。

Gate的作用:保持与客户端之间的长连接,将请求数据转发给后面的逻辑服务LogicServer。LogicServer最上面是一个消息路由服务Router,根据请求的类型转发到后面具体的逻辑服务器。其中c代表客户端,s代表服务器,g代表群组,因此比如c2c服务就是处理客户端之间消息的服务器,而auth服务是处理客户端登录请求的服务器。

逻辑类服务器与存储层服务打交道,其中:redis用于存储用户在线状态、用户路由数据(用户路由数据就是指用户在哪个gate服务上维护长连接),而DB用于存储用户的消息数据,这部分留待下一部分讲解。

以上的接入层、逻辑层由于本身不存储状态,因此都可以进行横向扩展。看似Gate维护着长连接,但是即使一个Gate宕机,客户端检测到之后可以重新发起请求接入另一台Gate服务器。

数据存储

路由数据:存放在Redis中,格式为(UID,客户端在哪个gate登录)。

消息数据:存储在DB中,部分也会缓存在缓存中方便查询,这部分做为下一部分文章的重点来讲解,不在这部分展开讨论。

核心交互流程

统一登录系统

登录授权(auth)

46a56b25115cfc205cd5dd2c7df3c8dd.png

客户端通过统一登录系统验证登录密码等。

SSO验证客户端用户名密码之后,生成登录token并返回给客户端。

客户端使用UID和返回的token向gate发起授权验证请求。

gate同步调用logic server的验证接口。

logic server请求SSO系统验证token合法性。

SSO向auth系统返回验证token结果。

如果验证成功,auth系统在redis中存储客户端的路由信息,即客户端在哪个gate上登录。

auth系统向gate返回验证登录结果。

gate向客户端返回授权结果。

登出(logout)

645e3bb884cb4b7128516366b4556535.png

客户端向gate发出logout请求。

gate设置客户端UID对应的peer无效,然后应答客户端登出成功。

gate向logic server发出登录请求。

处理该类请求的c2s服务器,清除redis中的客户端路由信息。

踢人(kickout)

用户请求授权时,可能在另一个设备(同类型设备,比如一台苹果手机登录时发现一台安卓手机也在登录这个账号)开着软件处于登录状态。这种情况需要系统将那个设备踢下线。

341eb082c200b8c383469e5525f6b2bb.png

新的客户端登陆流程同上面的登陆认证流程,只不过在auth模块完成认证之后,会做如下的操作:

根据UID到redis中查询路由数据,如果不存在说明前面没有登陆过,那么就像登陆流程一样返回即可。

否则说明前面已经有其他设备登陆了,将向前面的gate发送踢人请求,然后保存新的路由信息到redis中。

gate接收到踢人请求,踢掉客户端之后断掉与客户端的连接。

客户端上报消息(c2s消息)

7465a89f923e71d565a1f3293c058892.png

客户端向gate发送c2s消息数据。

gate应答客户端。

gate向逻辑服务器发送c2s消息。

logic server的c2s模块,将消息发送到MQ消息总线中。

appserver消费MQ消息做处理。

应用服务器推送消息(s2c消息)

e6d8b16d873d0dd7ea7468ee5a5999b8.png

业务服务器向逻辑服务器发送s2c消息。

逻辑服务器的s2c模块从redis中查询UID的路由数据,知道该用户在哪个gate上面登陆。

逻辑服务器向gate发送s2c消息。

gate服务器向客户端发送s2c消息。

客户端收到之后向gate ack消息。

gate向逻辑服务器ack s2c消息。

单对单聊天(c2c消息)

733e267fbd12ae3dba4c98c4f155506f.png

客户端向gate发送c2c消息。

gate向逻辑服务器发送c2c消息。

逻辑服务器的c2c模块保存消息到消息存储中,此时会将该消息的未读标志置位表示未读。

逻辑服务器应答gate,说明已经保存了该消息,即客户端发送成功。

gate应答客户端,表示c2c消息发送成功。

逻辑服务器的c2c模块,查询redis服务看该c2c消息的目标客户端的路由信息,如果不在线就直接返回。

否则说明该消息的目的客户端在线,向所在gate发送c2c消息。

gate向客户端转发c2c消息。

客户端向gate应答收到c2c消息。

gate向逻辑服务器应答客户端已经收到c2c消息。

逻辑服务器的c2c模块,在消息存储中清空该消息的未读标志表示消息已读。

注意第7步中,逻辑服务器的c2c模块在向gate转发c2c消息之后,需要加上定时器,如果在指定时间没有收到最后客户端的应答,需要重发。尝试几次重发都失败则放弃,等待下次用户登录了拉取离线消息。

群聊消息(c2g消息)

df100c83b670faf681e2d2f5266e19ff.png

客户端A向gate发送c2g消息。

gate向逻辑服务器发送c2g消息。

逻辑服务器的c2g模块将消息保存到SendMsg DB中,这部分消息将根据消息的发送者ID水平扩展。

c2g模块从cache中查询该群组的用户ID,如果查不到会到存放群组信息的DB中查询。

遍历获取到的群组ID,保存消息到RecvMsg DB中,这部分消息将根据接受者ID水平扩展。

查询redis,知道哪些群组用户当前在线。

向当前在线的用户所在gate发送c2g消息。

gate转发给客户端c2g消息。

客户端应答gate c2g消息。

gate应答逻辑服务器的c2g模块用户已经收到c2g消息。

c2g模块修改发送消息库该消息已读。

登录后拉取离线消息流程

28a635582d11cb5658a0724b5b66a860.png

客户端请求离线消息,其中会带上的字段是:客户端uid、当前客户端上保存的最大消息id(msgid)、每次最多获取多少离线消息(size)。当msgid为0的时候,由服务器自行查询当前的离线消息返回给客户端;否则服务器只会返回该消息id以后的消息。在这个例子中,假设第一次请求时,msgid为0,即由服务器查询需要给客户端返回哪些离线消息。

im服务器查询uid为100的用户的前10(因为size=10)的离线消息,具体来说就是去消息接收表中查询uid=100且read flag为false的前10条消息。这里假设第一次查询返回的消息中,最大消息id为100。

向客户端返回最新离线消息,同时带上最大离线消息id 100。

客户端收到离线消息只会,由于收到的消息数量等于size,说明可能还有没有读取的离线消息,因此再次向服务器查询,这一次带上的消息id为100,表示请求该id之后的未读消息。

IM服务器收到这一次拉取离线消息请求之后,由于msgid不为0,因此首先会将uid=100且msgid在100之前的未读消息全部置为已读。

获取uid=100且msgid>100的未读消息返回给客户端。

如果每次拉取的离线消息都等于拉取离线消息数量,客户端会一直重复拉取离线消息流程,直到拉取完毕。

协议设计

协议格式

998201fbd6dd45f783f15bb8b9199e12.png

协议分为包头和包体两部分,其中包体为固定的大小,包括:

version(4字节):协议版本号。

cmd(4字节):协议类型。

seq(4字节):序列号。

timestamp(8字节):消息的时间戳

body length(4字节):包体大小。

其中,包体部分使用protobuf来定义,以下介绍不同命令的包体格式。

认证(auth)

message AuthRequest {

string token = 1; // 从SSO服务器返回的登录token,登录之后保存在客户端

srting uid = 2; // 用户ID

}

message AuthResponse {

int32 status = 1; // 应答状态码,0表示成功,其他表示失败

string err_msg = 2; // 错误描述信息

}

登出(logout)

message LogoutRequest {

string token = 1; // 从SSO服务器返回的登录token,登录之后保存在客户端

srting uid = 2; // 用户ID

}

message LogoutResponse {

}

踢人(kickout)

message KickoutRequest {

enum Reason {

OTHER_LOGIN = 1; // 其他设备登录

}

int32 reason = 1; // 踢人原因

}

message KickoutResponse {

}

心跳包

无包体

单对单消息(c2c)

// 发送者发送消息的协议

message C2CSendRequest {

string from = 1; // 发送者

string to = 2; // 接收者

string content = 3; // 消息内容

}

message C2CSendResponse {

int64 msgid = 1; // 落地的消息ID

}

// 推送给接收者的协议

message C2CPushRequest {

string from = 1;

string content = 2;

int64 msgid = 3;

}

message C2CPushResponse {

int64 msgid = 1; // 消息id,服务器收到这个id可以去置位这个消息已读

}

群聊(c2g)

// 发送者发送群消息协议

message C2GSendRequest {

string from = 1; // 发送者

string group = 2; // 群

string content = 3; // 消息内容

}

message C2GSendResponse {

int64 msgid = 1; // 落地的消息ID

}

// 推送给其他群成员消息协议

message C2GPushRequest {

string from = 1; // 发送者

string group = 2; // 群

string content = 3; // 消息内容

int64 msgid = 4; // 落地的消息ID

}

message C2GPushResponse {

int64 msgid = 1; // 落地的消息ID

}

拉离线消息(pull)

message C2SPullMessageRequest {

string uid = 1;

int64 msgid = 2; // 拉取该消息id以后的离线消息,为0由服务器自行判断

int32 limit = 3; // 单次拉取离线消息的数量

}

message PullMsg {

string from = 1; // 发送者

int64 group = 2; // 目的群

string content = 3; // 消息内容

int64 msgid = 4; // 消息编号

int64 send_time = 5; // 服务器接收消息时间

}

message C2SPullMessageResponse {

repeated PullMsg msg = 1; // 离线消息数组

}

参考资料

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CSDN的IM系统设计包括以下几个方面: 1. 架构设计IM系统的架构设计需要考虑高并发、高可用性和可扩展性。常见的架构模式包括单服务器模式、集群模式和分布式模式,根据需求选择合适的架构。 2. 数据存储:IM系统需要存储用户信息、消息记录等数据。常见的数据存储方案包括关系型数据库(如MySQL)、NoSQL数据库(如MongoDB、Redis)和分布式文件系统(如HDFS)等。 3. 消息传输:IM系统需要实现实时消息传输功能。可以使用WebSocket协议进行长连接通信,也可以使用短轮询或长轮询等技术实现消息的推送。 4. 安全性设计IM系统需要考虑数据传输的安全性,包括用户身份认证、消息加密等。可以使用SSL/TLS协议保证数据传输的安全性。 5. 扩展性设计IM系统需要支持大规模用户和高并发请求。可以采用分布式缓存、负载均衡等技术来提升系统的扩展性和性能。 6. 用户状态管理:IM系统需要管理用户的在线状态、好友关系等。可以使用在线/离线状态标识、心跳机制等来管理用户状态。 7. 消息历史记录:IM系统需要保存用户的消息历史记录,以便用户查询和回溯。可以使用数据库或文件系统来存储消息记录。 8. 实时通知:IM系统需要实现消息的实时通知功能,包括声音、震动、推送等方式。可以使用推送技术(如苹果的APNS、谷歌的FCM)来实现实时通知。 以上是一些常见的IM系统设计要点,具体的设计还需要根据具体需求和技术选型进行深入的设计和实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值