设计一个聊天系统最主要是保证消息能够及时可靠的从一端传入到另外一端,同时要支持对历史消息的查看。按照同时聊天人数聊天系统可以分,一对一(one on one)和群聊(group chat),按照消息传递的及时性,可以分为实时发送(real-time message)和延迟发送(delay message),当发送者和接收者都在线,希望消息能够实时发送,当接受者不在线,需要将消息先保存起来,等接收者上线之后再发送。可以将这两个分类角度进行结合,分析设计时需要考虑的点。
对话场景分类
one on one & real-message
两个用户都在线,持续的发送消息,理想情况,A发送的消息B能立刻收到,延迟要低,传统的http请求无法满足请求,最好AB之间能建立一个长连接。
one on one & delaymessage
接受者当前不在线,接受者上线之后,能立刻接收到发送者发送的消息,考虑消息如何存储,消息推送时机。
group chat
A发送送的消息,能够让群里里面的其它人都接收到。
存在问题&解决思路
Q1:如何保证消息及时的快速推送?
可以在客户端和服务端使用WebSocket建立一个长连接,始终保持着连接的状态,不用每次消息发送的时候都要挥手创建链接,对应的服务就是一个有状态的服务。
Q2:如何对对离线的用户发送信息,如何传递?
对于每个用户,都要建立一个消息队列,里面存储了用户待投递的消息,当用户上线了之后,和服务端建立了连接之后,通过连接将消息推送给客户端,每次客户端收到消息之后,都要有一个成功接收的回值凭证,保证消息传递的可靠性。
Q3:如何感知用户是否在线,什么时间去尝试查用户链接是否创建成功?
如果采用服务器轮训的方式,会造成很多不必要的资源浪费,使用客户端上报心跳的方式,并且Q1中的连接是服务端发起的,当客户端开始上报心跳,服务端就会尝试创建一个长连接,过了一段时间客户端都没有上报心跳,服务端才会将对链接通道关闭,这样也避免如果让客户端发起链接,客户端可能因为不稳定的网络,频繁创建和端来链接。
Q4:多个设备情况下,如何保证所有消息都能够传递到所有设备?
对每个账号的关联的每个设备,都记录一个投递消息的最大号 cur_max_message_id,这样可以保证每次设备登陆时,建立连接,还未推送的消息接着推送。
Q4:一个大的整体架构是个什么样子?
为什么选择KV storge作为消息的存储,根据统计消息的查询和写入比例操作比例是1:1,为了快速查询,比如进行模糊文本查询,非结构化数据,查询速度要比使用结构化数据库查询效率要高。