当前位置:   article > 正文

聊聊IM系统的设计_im系统表结构的设计

im系统表结构的设计

目录

IM系统简介

IM系统实体组成

IM系统的特性

IM系统设计

IM系统分层架构

消息实时触达

短轮训场景

长轮训场景

websocket场景

TCP衍生出的IM传输协议

消息的可靠性

消息不丢失

消息不重复

消息的一致性

基于时钟的时序基准

基于全局发号器的时序基准

服务端整流

消息的安全性

传输安全性

HttpDNS防止DNS劫持

TSL链路安全性

数据存储安全

内容安全

消息通道管理

中心化存储

连接服务存储


在如今这个互联网社会,IM系统想必每个人都不陌生,甚至是我们生活不可或缺的一部分。大到微信、QQ、微博等,小到公司内部的客服系统、社区APP评论区,都有IM系统的影子。那么一个IM系统包含哪些部分?有哪些特性?系统架构如何设计呢?带着这些问题,我们今天就来聊一聊一个简易版IM系统的设计。

IM系统简介


IM系统实体组成

实体即为系统的元素,通过实体的特性以及实体之间的交互,组成了系统的功能链同时也使系统呈现出一定的特性。IM系统的实体大致可以分为:账号、联系人、关系、消息、会话。

账号:登录IM系统需要有一个账号,是用户的身份标识,账号除了包含基本的用户名/密码等信息,还可以包含头像、昵称等
关系:账号和账号之间通过加好友/互粉操作形成好友关系
联系人:好友列表或者联系人列表,统称为联系人
消息:不同账号之间发生聊天行为产生了消息
会话:同聊天对象的聊天记录组成了会话

IM系统的特性

实时性:发送的消息能实时送达没有延迟,实时性最直接影响用户体验的。消息的实时性即是产生一条消息后,如何保证快速的触达到用户。

可靠性:可靠性有两个方面,消息不重复、消息不丢失,是不是跟我们平时项目中使用的消息中间件一样,产生一条消息,别人来消费,既不能丢失消息也不能重复消费

一致性:这里主要是消息数据的一致性,消息顺序的一致、数据展示的一致

安全性:聊天通道的安全、聊天内容的安全、消息数据的安全
 

IM系统设计


IM系统分层架构

这里用一张IM系统的分层架构图来讲解的IM系统的设计:

接入终端:IM系统的使用者,接入媒介可以是PC、移动设备或者小程序等。
接入调度服务:这一层可能有些人不太理解,主要是负责管理接入端过来的链接请求,然后路由到后端的链接服务上。之所以需要这一层而不是直接访问连接服务是因为增加一定的调度能力,比如连接服务扩缩容,客户端需要重新连接的时候,通过这一层来调度,让连接层专注于自己的事情。
连接服务:管理来自客户端的链接,一般是长链接,维护和客户端的回话接收消息和推送消息。
业务服务:处理IM系统的核心业务,包括账号管理、联系人列表、用户的在线状态、消息的分发和存储、消息未读数业务逻辑处理。
存储服务:存储IM系统的核心数据,包括账号信息、消息数据、离线消息、以及富媒体消息等。
支撑服务:支撑服务为系统共有服务,比如网关、埋点服务、日志服务、配置中心等,贯穿整个系统。
基础设施:系统运行的基础设施,主要是部署相关,包括kubernetes、Nginx、CDN等 


消息实时触达

要保证消息能够实时触达给接收方,主要看消息推送的设计步骤,在IM系统发展历史上,消息的推送经过了以下几个阶段:

短轮训场景

短轮训的实现是通过客户端拉取的方式来实现,客户端定期、高频的从服务端来拉取消息,服务端收到客户端请求,如果有新消息就返回给客户端,如果没有就返回空列表,并且关闭连接。
优点:实现起来简单,方便比较容易落地
劣势:轮训的频率不好控制,频繁的HTTP请求会占用服务端很多资源

长轮训场景

长轮训也就是我们平时说的Long-polling的场景,这种技术在很多中间件中也有使用,比如配置中心Apollo就是基于长轮训实现的。相比短轮训无脑空跑的情况,长轮训在服务端没有数据的时候并不会立马返回,而是让连接hang住一段时间,在有数据的时候再返回给客户端。
优点:可以大幅降低短轮训场景下的客户端无用轮训频率
劣势:长时间的hang住请求,也是需要消耗服务端的资源,只是减少了一些请求的QPS而已

websocket场景

上面说的轮训场景还是基于请求-响应模式来实现,即客户端发出请求,等待服务端响应,本质上还是一种拉的方式,所有的请求只能由客户端发起。websocket的出现彻底解决了上面的问题,基于全双工的访问模式,连接的两端在建立连接之后都可以互相发送请求/响应,可以自主的进行。

优点
1、全双工通信,服务端有数据了就可以发送给客户端,不需要等待客户端请求,真正意义上的边缘触发,保证了消息的实时性。
2、数据交互的控制开销低,降低双方通信的网络开销。
缺点
1、网络异常处理:如果网络不稳定或断开连接,需要进行异常处理,以便保证WebSocket的可靠性。
2、安全性问题:websocket采用明文传输,需要采取一定的措施保证安全传输。 
 

TCP衍生出的IM传输协议
 

除了 WebSocket 协议,在 IM 领域,还有其他一些常用的基于 TCP 长连接衍生的通信协议,如 XMPP 协议、MQTT 协议以及各种私有协议,目前就有很多的大厂基于TCP来实现自有的传输协议。
优点
1、基于TCP的长链接通信,保证了通信的效率和消息推送的实时性。
2、自有协议可以结合自身业务的需要进行特殊流程的处理,做到真正意义上的高效和省流,同时在安全性上也有保障。
缺点:研发和使用有一定的成本。


消息的可靠性

我们参照消息中间件比如kafka的使用情形,消息的可靠性分为两部分:不丢失消息、没有重复消息,并且这两部分都涉及到客户端和服务端。我们可以画一个简单的消息发送时序图看看,从消息的发送到接收到消息这个过程中有哪些步骤是可能会出现问题的:
 

消息不丢失

  • 服务端消息丢失

    丢失场景
    :我们来分析下发送消息的步骤,在第1步客户端发送消息给服务端之后,连接服务将消息发给存储服务进行保存,然后返回客户端发送成功,这个过程1~4步每一步都可能会失败,失败的结果就是客户端收到发送失败的响应或者等待超时。

    解决办法:通过客户端的失败/超时重试机制保证消息不丢
     
  • 客户端消息丢失

    丢失场景
    :在第5步,服务端推送消息给客户端,这个时候服务端消息发送完成即为成功,但是这之后可能由于网络原因或者客户端的一些问题导致消息没有收到或者没有展示成功,这样在客户端就丢失了这条消息

    解决办法:通过客户端的ACK机制,服务端必须在收到客户端针对某条消息的ACK反馈之后才能确认这条消息发送成功,否则需要进行失败/超时重推

消息不重复

  • 服务端消息重复

    重复场景:
    服务端在第3或者4步失败,实际上消息已经成功存储,导致客户端重新发送消息。

    解决办法:服务端对消息做全局的去重处理,每个消息有一个全局唯一的msgId

  • 客户端消息重复

    重复场景:
    客户端反馈了ACK响应,但是服务端没有接收到,导致服务端重传

    解决办法:客户端基于消息Id做去重处理

     

消息的一致性

这里重点讲下消息的顺序一致,接受方接收到的消息的顺序必须和发送方发送消息的顺序一致,否则就可能存在语义错乱的问题,那么我们来看看有哪些方法可以保证消息的顺序性。


基于时钟的时序基准

所谓顺序即是消息的先后,最容易想到的方案就是通过时钟来确定,每次发送一条消息,为该条消息打上一个时间戳编号,在推送或者接收方来拉取消息的时候,按照时间的先后顺序来排序消息。
问题:时钟不好统一,以发送设备的时间没办法保证时钟的统一,以IM服务的时间为准,在集群环境下,多台机器时钟可能也会存在微小的差别。


基于全局发号器的时序基准

除了时钟另外一种实现方式是基于全局发号器来实现,给每个消息都编上号码,根据消息的接收顺序,消息编号从小到大依次递增。
问题:全局发号器需要考虑全局情况下的唯一递增特性,服务本身需要保证高可用,可以考虑Redis的incr或者snowflake算法,不过这里的全局可以是单个回话或者某个群聊的范围,不同的回话之间没必要考虑保证顺序。


服务端整流

除了上述两种措施来保证消息的顺序性之外,服务端自身可以通过整流来保证消息的顺序性。整流主要是针对的离线消息,在给客户端推送离线消息的时候,服务端会将多条消息进行打包,每个消息都有同一个packageID,然后包里面的消息按照消息的先后进行排序,一起发送给客户端,这样也可以保证消息的顺序性。
 

消息的安全性

IM系统作为一个大型人人交互的聊天系统,是非常涉及用户隐私的,所以消息的安全性是很重要,很容易将用户聊天内容泄露。安全性主要有以下几个方面:


传输安全性

用户发送消息给服务端或者服务端推送消息给客户端,这里都涉及通道传输的安全性,是否有被劫持的风险,比如我们常说的websocket传输是基于明文传输的,需要采取一些安全加密措施才可以。下面列举几个常用的安全措施:

HttpDNS防止DNS劫持

DNS劫持是一种很场景的攻击手段,对于IM系统,消息服务一般是会暴露一个公网的接入入口,用户访问的时候都需要经过DNS域名解析服务。攻击者一般会通过攻击DNS服务或者伪造DNS服务导致用户无法访问DNS服务或者访问到恶意的IP。

  • LocalDNS:运营商自己搭建的DNS服务,一般运营商为了提高访问速率,会在LocalDNS缓存解析数据,通常由Internet服务提供商(ISP)或局域网管理员管理,因此容易受到恶意攻击或篡改。如果本地DNS服务器被替换或篡改,所有使用该DNS服务器的设备都可能会受到攻击。
  • HttpDNSHttpDNS是通过Http请求去权威DNS服务器去请求解析数据,这种方式可以避免本地DNS被劫持的情况。



TSL链路安全性

消息在传输链路中可能存在的安全隐患,基本可以分为终端、劫持、篡改和伪造几种,所以保证消息的安全传输是安全通信的基本条件,这里介绍一下比较常用的TSL链路安全性传输。
 


TSL又称SSL,是基于传输层之上做的一种安全防护,TLS协议可以与HTTP、SMTP、POP3、IMAP等应用层协议配合使用。TLS协议的主要功能包括身份验证、加密和完整性保护。TSL主要包含以下部分:
协商:也就是我们通常所说的"握手",协商主要是确认对方的身份,这里会用到CA签名认证,保证身份不被伪造,然后协商加密的方式和公钥。
通信:经过协商之后,双方已经确认了身份和加密方式,就可以用协商的秘钥对传输数据进行加密保证通信的安全性。

关于传输安全的在我之前的一篇安全认证文章中有比较多的讲解,有兴趣的可以去看看:WEB安全认证


数据存储安全

账号、消息数据存储在服务端也需要考虑安全性,对于一些私密的消息内容和用户隐私数据,如果出现内部人员非法查询或者数据库被“拖库”,可能会导致隐私信息的泄露。这里可以用一些加密或者脱敏的手段来保证安全性,比如密码加密,通常可以用散列法来实现,消息的数据也可以经过脱敏处理。


内容安全

内容安全也是很重要的一个部分,IM系统本质上也是一个消息传播的系统,不安全的内容也会造成负面的社会影响,比如一些恶意造谣、黄色以及政治敏感话题等,内容安全的保障可以依托以下手段:
敏感词库:建立敏感词库,采用撞库的方式来判断一些恶意和敏感的问题,这个对一些敏感信息比较有效。
图片识别依托图片识别技术对色情图片和视频、广告图片、涉政图片等进行识别处置。
爬虫通过爬虫技术对消息中的连接信息内容进行爬取,识别恶意链接。


消息通道管理

要保证消息能准确发送给接收方,就需要识别到接收方到IM服务的连接通道,也就是channel,只有通过channel我们才能把消息发送到客户端,在客户端建连环节会生成channel,channel适合连接的机器一一映射的,针对接收方的消息,我们必须要发送到这台机器上,然后通过该机器上的channel才能发送给接收方,这里有两种方案供选择:

中心化存储

使用一个中心化的存储来维护用户和机器的映射关系,精准的确认用户的链接状态是在哪台机器上,当有消息产生的时候,通过用户ID可以精准找到一台某台连接机器,然后将消息推送到这台机器上。
 

优点:有中心化存储统一管理连接状态,消息可以精准的找到某一台机器。
缺点:推送消息的时候需要先查询连接状态信息表,找到映射关系,会增加连接中心的访问压力,同时也会增加推送消息链路的耗时。
适用场景:点对点聊天,连接状态单一,好维护
 

连接服务存储

这种情况是连接服务不上报连接信息,状态自己维护,所有的链接机器都去订阅消息,当有消息产生时,会给每一台机器发送一份,然后连接服务判断自己的连接信息是否需要发送,如果不需要就丢弃消息。
 

优点:去中心化存储,连接服务自己维护连接状态,不需要单独服务来维护了。
缺点:消息的冗余,连接机器会收到很多自己不需要的消息。
适用场景:群聊、直播场景 



想了解更多有关架构设计方面的知识关注作者个人技术公众号:八阿哥技术圈,干货多多,共同学习,共同进步!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/673370
推荐阅读
相关标签
  

闽ICP备14008679号