IM聊天室设计


简述

即时聊天系统(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 = {
    timeout3000,
    timeoutObjnull,
    serverTimeoutObjnull,
    startfunction(){
        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)
    }
}

消息可靠传输设计

消息的可靠传输是指消息的不丢失和不重复,聊天应用消息分发策略采用基于服务器的路由转发。但数据在传输过程中,当网络不稳定或机器宕机时,会造成消息丢失等情况。

IM聊天室设计

为防止网络不稳定或机器宕机,从而造成消息丢失,我这里引进消息确认机制,即发送消息时,可将消息存一个副本,设置一个超时时间。当消息在规定时间内成功到达时,通知删除该消息,否则进行重发。可能由于机器处于宕机状态一直未响应,导致频繁重发,因此需要做数据幂等性,防止产生脏数据。同时需要校验消息完整性。



原文始发于微信公众号(行走318川藏线):IM聊天室设计

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/20925.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!