简述
即时聊天系统(IM),本次主要介绍的包括技术选型、消息设计、重连机制、消息可靠性。
技术选型
MongoDB
由于IM实时性要求较高,且IM产生的数据量较大,数据关联性强,而关系型数据库不适用存储海量数据,对于复杂的数据结构,需要拆分成多个表进行存储,从而会导致查询效率低下、成本高。存储不规则的数据集,而非关系型数据库MongoDB将是最佳选择。
MongoDB适应场景:
-
网站数据:Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。 -
缓存:由于性能很高,Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo 搭建的持久化缓存层可以避免下层的数据源过载。 -
大尺寸、低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。 -
高伸缩性的场景:Mongo 非常适合由数十或数百台服务器组成的数据库,Mongo 的路线图中已经包含对MapReduce 引擎的内置支持。
Redis
使用zset有序列表存储聊天列表,方便根据时间进行排序
使用bitmap布隆过滤器记录用户在线状态,方便统计在线用户量,使用 bitmap 是一个节约空间且效率又高的一种方法
消息设计
包括:消息内容、聊天列表
1.消息内容
消息主体结构设计
字段 | 说明 |
---|---|
msgId | 消息id |
msgType | 消息类型(1:文本消息;2:图片消息;3:语音消息;4:视频消息;5:地理位置消息) |
sessionType | 会话类型(1私聊;2群聊) |
from | 发送者 |
to | 接收者 |
sendTime | 发送时间 |
msgStatus | 消息状态(1已发送;2未抵达;3已抵达) |
msgBody | 消息体 |
私聊与群聊单独存储,这样会提示检索效率、减少单表存储压力。
msgBody设计
1.文本消息
字段 | 说明 |
---|---|
text | 文本内容 |
2.图片消息
字段 | 说明 |
---|---|
picUrl | 图片地址 |
3.语音消息
字段 | 说明 |
---|---|
voiceUrl | 语音地址 |
format | 语音格式,如amr,speex等 |
size | 大小 |
4.视频消息
字段 | 说明 |
---|---|
videoUrl | 视频地址 |
format | 视频格式 |
size | 大小 |
5.地理位置消息
字段 | 说明 |
---|---|
locationX | 地理位置纬度 |
locationY | 地理位置经度 |
scale | 地图缩放大小 |
label | 地理位置信息 |
2.聊天列表
字段 | 说明 |
---|---|
id | 列表id |
sessionType | 会话类型 |
owner | 对话所属用户 |
other | 对话关联用户(会话类型为私聊,other是用户id,为群聊,other是群id) |
unread | 未读消息数量 |
recentMsgId | 最后一条未读消息 |
createdTime | 创建时间 |
modifiedTime | 更新时间 |
WebSocket重连技术
在IM应用中,WebSocket提供了一种全双工的通信机制,为了提升聊天系统等实际应用场景下的消息即时性和可靠性,需要克服WebSocket及其底层依赖的TCP连接对于复杂网络情况下的不稳定性,即采用断网重连的策略。
如何检测需要重连?
采用心跳机制去轮询检测网络的状态,如果超过一定时间后未收到服务器回应,则任务服务不可用,为了不在密集的心跳下影响到实际的业务,心跳包发送频率稍慢一些,例如:60s/次,然后再网络状态由离线变为在线时立即发送一次心跳,检测当前连接是否可用,不可用则进行重连处理。
心跳检测机制如何实现,有何意义?
定时给服务端发送ping包。假设这里三秒发送一次心跳,那么其意义何在?有时间网络断开了,但是这个时候服务端并没有触发onclose的事件,此时无法实时知道客户端和服务端是否处于正常的链接状态, 导致服务器会继续向客户端发送多余的链接,并且操作数据包的丢失。心跳机制是每隔一段时间会向服务器发送一个数据包,告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了。需要重连
具体实现部分代码如下:
// 建立连接成功时进行通信,传入用户信息
socket.onopen = function () {
// Web Socket 已连接上,使用 send() 方法发送数据
let msgData = {"type": "connect","messageContent":mine.username + "->已上线","sender":mine.id,"senderName":mine.username, "avatar": mine.avatar};
socket.send(JSON.stringify(msgData));
//心跳检测重置
heartCheck.start();
};
/**
* 重新连接
**/
function reconnect(url){
//避免重复创建连接
if (lockReconnect){
return ;
}
lockReconnect = true;
//没连接上会一直重连节,设置延迟避免请求过多
overTime && clearTimeout(overTime);
//如果网络断开,执行reconnect,使用定时器,4秒后重新创建一个新的websocket连接
overTime = setTimeout(function (){
createWebSocket(url);
lockReconnect = false;
}, 4000);
}
//心跳检测
var heartCheck = {
timeout: 3000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
let self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
let msgData = {"type": "heart","messageContent":"ping"};
socket.send(JSON.stringify(msgData));
//连接超时,关闭连接
self.serverTimeoutObj = setTimeout(function() {
socket.close();
}, self.timeout);
}, this.timeout)
}
}
消息可靠传输设计
消息的可靠传输是指消息的不丢失和不重复,聊天应用消息分发策略采用基于服务器的路由转发。但数据在传输过程中,当网络不稳定或机器宕机时,会造成消息丢失等情况。
为防止网络不稳定或机器宕机,从而造成消息丢失,我这里引进消息确认机制,即发送消息时,可将消息存一个副本,设置一个超时时间。当消息在规定时间内成功到达时,通知删除该消息,否则进行重发。可能由于机器处于宕机状态一直未响应,导致频繁重发,因此需要做数据幂等性,防止产生脏数据。同时需要校验消息完整性。
原文始发于微信公众号(行走318川藏线):IM聊天室设计
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/20925.html