浅析怎么开发分布式IM即时通讯系统

Posted wecloud1314

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析怎么开发分布式IM即时通讯系统相关的知识,希望对你有一定的参考价值。

本文不会给出一套通用的IM方案,也不会评判某种架构的好坏,而是讨论设计IM系统的常见难题跟业界的解决方案。

因为也没有所谓的通用IM架构方案,不同的解决方案都各有其优缺点,只有最满足业务的系统才是一个好的系统。

在人力、物力、时间资源有限的前提下,通常需要做出很多权衡,此时,一个能够支持快速迭代、方便扩展的IM系统才是最优解。

IM常见术语

0)用户:系统的使用者。

1)消息:是指用户之间的沟通内容(通常在IM系统中,消息会有以下几类:文本消息、表情消息、图片消息、视频消息、文件消息等等)。

2)会话:通常指两个用户之间因聊天而建立起的关联。

3)群:通常指多个用户之间因聊天而建立起的关联。

4)终端:指用户使用IM系统的机器(通常有android端、ios端、Web端等等)。

5)未读数:指用户还没读的消息数量。

6)用户状态:指用户当前是在线、离线还是挂起等状态。

7)关系链:是指用户与用户之间的关系,通常有单向的好友关系、双向的好友关系、关注关系等等(这里需要注意与会话的区别:用户只有在发起聊天时才产生会话,但关系并不需要聊天才能建立。对于关系链的存储,可以使用图数据库(Neo4j等等),可以很自然地表达现实世界中的关系,易于建模)。

8)单聊:一对一聊天。

9)群聊:多人聊天。

10)客服:在电商领域,通常需要对用户提供售前咨询、售后咨询等服务(这时,就需要引入客服来处理用户的咨询)。

11)消息分流:在电商领域,一个店铺通常会有多个客服,此时决定用户的咨询由哪个客服来处理就是消息分流(通常消息分流会根据一系列规则来确定消息会分流给哪个客服,例如客服是否在线(客服不在线的话需要重新分流给另一个客服)、该消息是售前咨询还是售后咨询、当前客服的繁忙程度等等)。

12)信箱:本文的信箱我们指一个Timeline、一个收发消息的队列。

读扩散
需要注意与Feeds系统的区别:在Feeds系统中,每个人都有一个写信箱,写只需要往自己的写信箱里写一次就好了,读需要从所有关注的人的写信箱里读。但IM系统里的读扩散通常是每两个相关联的人就有一个信箱,或者每个群一个信箱。

读扩散的优点:

    1)写操作(发消息)很轻量,不管是单聊还是群聊,只需要往相应的信箱写一次就好了;
    2)每一个信箱天然就是两个人的聊天记录,可以方便查看聊天记录跟进行聊天记录的搜索。


读扩散的缺点:读操作(读消息)很重,在复杂业务下,一条读扩散消息源需要复杂的逻辑才能扩散成目标消息。即时通讯聊天软件app开发可以加蔚可云的v:weikeyun24咨询

接下来看看写扩散。

在写扩散中,每个人都只从自己的信箱里读取消息。

但写(发消息)的时候,对于单聊跟群聊处理如下:

    1)单聊:往自己的信箱跟对方的信箱都写一份消息,同时,如果需要查看两个人的聊天历史记录的话还需要再写一份(当然,如果从个人信箱也能回溯出两个人的所有聊天记录,但这样效率会很低);
    2)群聊:需要往所有的群成员的信箱都写一份消息,同时,如果需要查看群的聊天历史记录的话还需要再写一份。可以看出,写扩散对于群聊来说大大地放大了写操作。

写扩散优点:

    1)读操作很轻量;
    2)可以很方便地做消息的多终端同步。


写扩散缺点:写操作很重,尤其是对于群聊来说(因为如果群成员很多的话,1条消息源要扩散写成“成员数-1”条目标消息,这是很恐怖的)。

在Feeds系统中:

    1)写扩散也叫:Push、Fan-out或者Write-fanout;
    2)读扩散也叫:Pull、Fan-in或者Read-fanout。

通常情况下,ID设计主要有以下几大类:

    1)UUID;
    2)基于Snowflake算法的ID生成方式;
    3)基于申请DB步长的生成方式;
    4)基于Redis或者DB的自增ID生成方式;
    5)特殊的规则生成唯一ID。

在IM系统中需要唯一Id的地方主要是:

    1)聊天会话ID;
    2)聊天消息ID。

消息ID不递增可以吗?

我们先看看不递增的话会怎样:

    1)使用字符串,浪费存储空间,而且不能利用存储引擎的特性让相邻的消息存储在一起,降低消息的写入跟读取性能;
    2)使用数字,但数字随机,也不能利用存储引擎的特性让相邻的消息存储在一起,会加大随机IO,降低性能;而且随机的ID不好保证ID的唯一性。


因此,消息ID最好是递增的。

全局递增 vs 用户级别递增 vs 会话级别递增:

全局递增:指消息ID在整个IM系统随着时间的推移是递增的。全局递增的话一般可以使用Snowflake(当然,Snowflake也只是worker级别的递增)。此时,如果你的系统是读扩散的话为了防止消息丢失,那每一条消息就只能带上上一条消息的ID,前端根据上一条消息判断是否有丢失消息,有消息丢失的话需要重新拉一次。

连续递增 vs 单调递增:

连续递增是指ID按 1,2,3...n 的方式生成;而单调递增是指只要保证后面生成的ID比前面生成的ID大就可以了,不需要连续。

据我所知:QQ的消息ID就是在会话级别使用的连续递增,这样的好处是,如果丢失了消息,当下一条消息来的时候发现ID不连续就会去请求服务器,避免丢失消息。

此时,可能有人会想,我不能用定时拉的方式看有没有消息丢失吗?当然不能,因为消息ID只在会话级别连续递增的话那如果一个人有上千个会话,那得拉多少次啊,服务器肯定是抗不住的。

对于读扩散来说,消息ID使用连续递增就是一种不错的方式了。如果使用单调递增的话当前消息需要带上前一条消息的ID(即聊天消息组成一个链表),这样,才能判断消息是否丢失。

写扩散:信箱时间线ID使用用户级别递增,消息ID全局递增,此时只要保证单调递增就可以了。

读扩散:消息ID可以使用会话级别递增并且最好是连续递增。

以上是关于浅析怎么开发分布式IM即时通讯系统的主要内容,如果未能解决你的问题,请参考以下文章

浅析大型IM即时通讯系统开发难度

im即时通讯开发:浅析分布式架构下的负载均衡技术

浅析即时通讯移动端 IM 开发中登录请求的优化

浅析IM即时通讯开发移动端DNS域名劫持问题

浅析im即时通讯开发加白名单

浅析IM即时通讯开发出现上网卡顿?网络掉线?