较常见的IM工具
微信 qq 支付宝 企业微信 钉钉 飞书
消息收发流程示意图
IM的核心点
- 一是消息传输的速度要快, 涉及延时方面的问题;
- 第二个是要保证消息的送达率。
- IM 产品的用户量和活跃度通常都很大,在一些特殊的时间点经常容易造成流量的波峰,因此技术上需要能够应对突发的量级。所以在前期需要设计好弹性扩容,对系统的伸缩能力提前做好设计。
- IM 包含用户的大量隐私,一旦被黑客攻破不堪设想,同时在公共安全方面的影响也越来越受重视,因此很多 IM 产品都投入精力做内容安全、平台可控方面的工作。
- 同时,现在用户的设备多样化,IM 通常需要支持多设备,又涉及到一个多终端消息同步的问题。(得物是否支持暂时有待验证)
围绕以上几点核心关注点来一一讨论
一、是消息传输的速度要快,涉及延时方面的问题:
推荐阅读:IM系统的前世今生——2小时快速搭建高性能、可拓展的IM系统
在使用 TCP 长连接的 IM 服务设计中,往往都会涉及到心跳。心跳一般是指某端(绝大多数情况下是客户端)每隔一定时间向对端发送自定义指令,以判断双方是否存活,因其按照一定间隔发送,类似于心跳,故被称为心跳指令。
- IM中保持有效长连接的重要性:
在当前链接可用的情况下,每一次请求都只是简单的数据发送和接受,不需要进线DNS解析,链接建立等时间,很大程度上加快了请求的速度,但是前提是链接可用,连接保持的前提是检测连接的可用性,并且在连接不可用时主动放弃当前连接并建立新的连接。
链接如果一定时间内不发送消息有可能会被回收.对于服务器来说,要定时清理无效的链接来减轻负载,另外业务也需要及时的清理不需要的链接来处理掉线问题.
- 为什么用了长链之后还有使用心跳:
TCP是一个基于连接的协议,连接状态是由一个状态机制进行维护,链接完成后,双方都会处于established状态,在这之后状态不会主动进行变化,这就是说如果不进行任何的调用,TCP链接一直处于空闲,虽然没有任何数据传输也是保持连接状态,即使中间路由崩溃重启很多次.
有人会想的 TCP不是KeepAlive机制么? TCP的KeepAlive机制开启后TCP层会在设定的时间到了之后发送探针检测链接的可用性,一般时间是7200秒,失败之后重试10次,每次的超时时间是75秒,而且TCP的Keep机制只是检测长链的可用性,尔心跳可以实时检测通信链接的双端的状态.
比如说某一台服务器因为过载处于卡死的状态,但是TCP的KeepAlive机制扔能确认链接状态.这种情况下消息肯定是发送失败,但是仍然会持续的发送这些必然失败的消息。
所以权衡之下还是需要用心跳的方式还确认通信双方的在线状态.
心跳发送的同时也可以携带其他信息,可以为通信提供更灵活更准确的保证
二、保证消息的送达率
- ACK机制
消息类型分为以下两种:
- 一种是在线消息:在线消息是指双方用户都处于实时在线的情况,在网络中实时送达;
- 另一个是离线消息:如果用户当时不在线,可能处于暂时离线的状态,我们把消息在缓存中存下来,缓存的消息可以保证更高的读取效率,用户下次上线的时候可以很快的取回来。
但仅仅靠在线和缓存是不够的,因为缓存可能会丢,网络可能出现会丢包,所以我们在数据库里储存关键消息。这类的消息是强一致性的要求,用户发送完成之后,服务端必须要确认数据被存入关键数据库里,否则客户端上的表现是消息未发送成功,是可以触发到上层去从事这种机制的。
在TCP这一层,所有sender发送的消息数据,每一个字节都有标号,每个字节在抵达接受端之后接收端会返回一个确认信息(ACK Number),两者的关系为Ack = seq+1。如果发送者发送的消息为seq=1,长度为100bytes的包,那么网关会返回一个ack = 101的包,如果发送者收到了这个ACK包,说明发送成功,否则发送者方面会进行重试机制 重新发送上面的包. (章鱼目前是3次重试)。
有人会有疑问,TCP本身是具备可靠性的,为什么还会出现消息丢失的情况?
在数据安全的抵达网络层之后,还需要一层层的往上移交处理,比如说:敏感词校验,写DB,前端页面展示,存入cache,以及一些突发状况的发生:断网,用户退出,关机等等.
- 应用层seq ID
给每一个消息都分配seq ID , 对于单个用户的接受消息队列来说seqID是连续的,如果消息A 和 消息B 是相邻的,那么 MsgBSeqID = MsgASeqID + 1, 每次存入DB的时候更新数据库里的序号为上一条+1.
如果出现不联系的seq ID时说明消息丢失了,可以进行重新发送
这么做的好处是不用每条信息都发送一条ACK消息,坏处是如果出现了不连续的seq ID,会过度依赖refetch,较难定位出现问题的原因.如果refetch过于频繁,流量消耗很有可能会大于ACK消息的数据量
要实现可靠性,需要先了解每一个环节以及数据如何在各个环节流动,然后才是分析每一个环节数据出错的可能性。检验可靠性的标准:入袋为安,存入 db 或者以其他方式持久化到 disk 当中之后,才能保证用户每次都能正确读取到消息。
可靠性可以理解为两方面:
- 一是数据可靠抵达(没有任何中间数据被丢失)
- 二是正确抵达(没有乱序或者数据更改)
消息在发送接收过程中,能够做到不丢消息、消息不重复两点。
三、流量波峰
跨网和弱网的环境下,所以传输协议非常关注对消息的压缩,以及 网络 带宽的占用,网易云信在这方面也做了很多的工作。这也和标准协议有差别,标准协议的消息结构都是 JSON 或 XML 格式,承载同样的有效内容,最终呈现出来的消息体会变得非常庞大,但在这一块私有协议可以做得非常好。
四、安全
通信过程中所有数据序列化的算法、加密的机制,以及加密的级别,全都是自己定义的。同时也考虑到,在整个传输的过程中可能长期存在的安全风险,比方第三方的攻击,以及数据在网络流转过程中被拷贝和重放的潜在安全风险,这些在设计过程中都需要被规避掉。
五、多点登录和消息漫游
什么是多点登录?
以微信为例:可以PC端、phone端同时登录、同时收发消息。需要注意的是:一个端只能登录一个实例,例如同一个QQ号,在pc1上登录再到pc2上登录,后者会把前者踢出,pc1会收到通知“你已在别处登录xxoo”。比如在微信上两个手机同时登陆同一个账号时就会踢出前一个登陆并给出提示
什么是消息漫游?
在当前终端登录时产生的聊天记录,切换到任意一终端时都能够拉取到历史的聊天记录,这就是消息漫游
关于IM还有很多需要去研究和探索的东西。欢迎有兴趣的伙伴们一起交流沟通