好消息:IM1.0.0版本已经上线啦,支持特性:mysql
首先讲讲IM(即时通信)技术能够用来作什么:
聊天:qq、微信
直播:斗鱼直播、抖音
实时位置共享、游戏多人互动等等
能够说几乎全部高实时性的应用场景都须要用到IM技术。git
本篇将带你们从零开始搭建一个轻量级的IM服务端,麻雀虽小,五脏俱全,咱们搭建的IM服务端实现如下功能:github
这个项目涵盖了不少后端必备知识:redis
咱们先从最简单的特性开始实现:一个普通消息的发送
消息格式以下:sql
message ChatMsg{ id = 1; //消息id fromId = Alice //发送者userId destId = Bob //接收者userId msgBody = hello //消息体 }
如上图,咱们如今有两个用户:Alice和Bob链接到了服务器,当Alice发送消息message(hello)
给Bob,服务端接收到消息,根据消息的destId进行转发,转发给Bob。docker
那咱们要怎么来实现回执的发送呢?
咱们定义一种回执数据格式ACK,MsgType有三种,分别是sent
(已发送),delivered
(已送达), read
(已读):数据库
message AckMsg { id; //消息id fromId; //发送者id destId; //接收者id msgType; //消息类型 ackMsgId; //确认的消息id } enum MsgType { DELIVERED; READ; }
当服务端接受到Alice发来的消息时:后端
sent(hello)
表示消息已经被发送到服务器。message AckMsg { id = 2; fromId = Bob; destId = Alice; msgType = SENT; ackMsgId = 1; }
hello
转发给Bob后,马上向Alice发送delivered(hello)
表示消息已经发送给Bob。message AckMsg { id = 3; fromId = Bob; destId = Alice; msgType = DELIVERED; ackMsgId = 1; }
read(hello)
表示消息已读message AckMsg { id = 4; fromId = Bob; destId = Alice; msgType = READ; ackMsgId = 1; }
这个消息会像一个普通聊天消息同样被服务器处理,最终发送给Alice。
缓存
在服务器这里不区分ChatMsg
和AckMsg
,处理过程都是同样的:解析消息的destId
并进行转发。安全
当用户量愈来愈大,必然须要增长服务器的数量,用户的链接被分散在不一样的机器上。此时,就须要存储用户链接在哪台机器上。
咱们引入一个新的模块来管理用户的链接信息。
模块叫作user status
,共有三个接口:
public interface UserStatusService { /** * 用户上线,存储userId与机器id的关系 * * @param userId * @param connectorId * @return 若是当前用户在线,则返回他链接的机器id,不然返回null */ String online(String userId, String connectorId); /** * 用户下线 * * @param userId */ void offline(String userId); /** * 经过用户id查找他当前链接的机器id * * @param userId * @return */ String getConnectorId(String userId); }
这样咱们就可以对用户链接状态进行管理了,具体的实现应考虑服务的用户量、指望性能等进行实现。
此处咱们使用redis来实现,将userId和connectorId的关系以key-value的形式存储。
除此以外,还须要一个模块在不一样的机器上转发消息,以下结构:
此时咱们的服务被拆分红了connector
和transfer
两个模块,connector
模块用于维持用户的长连接,而transfer
的做用是将消息在多个connector
之间转发。
如今Alice和Bob链接到了两台connector上,那么消息要如何传递呢?
机器[1]
上时
user status
的online
方法记录Alice上线。机器[1]
收到消息后,解析destId,在内存中查找是否有Bob。transfer
。transfer
调用user status
的getConnectorId(Bob)
方法找到Bob所链接的connector,返回机器[2]
,则转发给机器[2]
。流程图:
user status
模块管理用户链接,transfer
模块在不一样的机器之间转发,使服务能够水平扩展。transfer
须要和每台connector
机器都保持长连接。若是用户当前不在线,就必须把消息持久化下来,等待用户下次上线再推送,这里使用mysql存储离线消息。
为了方便地水平扩展,咱们使用消息队列进行解耦。
transfer
接收到消息后若是发现用户不在线,就发送给消息队列入库。用户的注册登陆、帐户管理、好友关系链等功能更适合使用http协议,所以咱们将这个模块作成一个restful服务,对外暴露http接口供客户端调用。
至此服务端的基本架构就完成了:
以上就是这篇博客的全部内容,本篇帮你们构建了IM服务端的架构,但还有不少细节须要咱们去思考,例如:
更多细节实现就留到下一篇啦~
IM1.0.0版本已上线,github连接:
https://github.com/yuanrw/IM
以为对你有帮助请点个star吧~!