消息存储

大部分IM为便于查看历史消息或暂存离线消息,都需对消息进行服务端存储,那怎么存储或暂存。

消息索引和消息内容

点对点消息存储

点对点消息的参与方:

  • 消息发送方
  • 消息接收方

收发双方的历史消息相互独立:发送方删除某条消息,接收方仍可获取到这条消息。

所以库表设计,需要索引表中收发双方各有一条自己的索引记录:

  • 消息发送方的发件箱索引
  • 消息接收方的收件箱索引

收发双方看到的消息内容其实一致,因此还需一个独立的消息内容表
消息内容表用于存储消息维度的一些基本信息,比如:

  • 消息ID
  • 消息内容
  • 消息类型
  • 消息产生时间

收发双方的两个索引表通过同一个消息ID和这个内容表关联。

假设张三给李四发送一条消息,消息存储在MySQL,那么上面涉及的两张表大致如下:

内容表

索引表

张三给李四发了一条“你好”,该动作会向内容表存储一条消息,消息内容:
ID为1001,消息内容是“你好”,消息类型是文本消息,还有当时消息创建的时间。
同时往索引表里存储两条记录:

  • 张三的索引:内容有会话对方的UID(李四的UID),是发件箱的索引(也就是0),同时记录这条消息的内容表里的消息ID为1001
  • 李四的索引:内容有会话对方的UID(张三的UID),是收件箱的索引(也就是1),同样也同时记录这条消息的内容表里的消息ID为1001

联系人列表

最近联系人列表,让互动双方快速查找需聊天对象,联系人列表一般还会携带两人最近的一条聊天消息用于前端展示。

和消息索引表的存储逻辑相比,联系人列表在存储上有以下区别。

  • 联系人列表只更新存储收发双方的最新一条消息,不存储两人所有的历史消息
  • 消息索引表的使用场景一般用于查询收发双方的历史聊天记录,是聊天会话维度
  • 联系人表的使用场景用于查询某一个人最近的所有联系人,是用户全局维度。
    在库表的设计上,联系人列表的存储实际和消息索引表类似,只不过消息索引表在接收到消息时,大部分情况都是插入操作,而联系人列表很多时候是更新操作。

最近联系人表


张三给李四发完消息后,除在内容表索引表插入记录,还会更新各自的最近联系人表

比如更新张三的最近联系人表,如果和李四之前

  • 无聊天记录
    则新插入一条联系人记录。联系人的对方UID为李四的UID,和这个联系人最新的一条消息ID是1001
  • 有过聊天记录
    则只需更新张三和李四的最新的一条聊天消息ID为1001,同样的办法再更新一次李四的联系人列表

2 消息收发通道

如何将消息发出去?
怎么把消息投递给接收方?
这里涉及两条通道:

  • 消息发送通道
    发送方通过发送通道把消息从本地发送到IM服务端
  • 消息接收通道
    IM服务端通过接收通道把消息投递给接收方

2.1 消息发送通道

实现方案

  • IM服务端提供一个HTTP协议的API接口,客户端需发送消息时,调用该接口把消息发给IM服务端
  • 客户端和IM服务端维护一个TCP长连接,客户端有消息发送时,以私有协议封装这条要发送的消息,然后通过TCP长连接把消息发给IM服务端

IM服务端提供消息发送的API,发送方可通过任意方式调用到这个API,把消息发出去。

2.2 消息接收通道

非P2P模式IM系统,由于有一条消息要投递给某个接收方这个事件,接收方并没有办法能实时知道,只有IM服务端收到发送方发出的消息时能实时感知到,因此消息投递这个动作一般都是IM服务端触发。

下面,我画了一张图来说明接收通道的业务逻辑,目前业界在消息接收通道的实现上较多采用的方式是下面这样的。

解释一下这张图。

IM服务端的网关服务和消息接收方设备之间维护一条TCP长连接(或者Websocket长连接),借助TCP的全双工能力,也就是能够同时接收与发送数据的能力。当有消息需要投递时,通过这条长连接实时把消息从IM服务端推送给接收方。

对于接收方不在线(比如网络不通、App没打开等)的情况,还可以通过第三方手机操作系统级别的辅助通道,把这条消息通过手机通知栏的方式投递下去。

这里简单解释一下,常见的第三方操作系统级别的辅助通道。比如苹果手机的APNs(Apple Push Notification Service)通道、Android手机的GCM通道,还有各种具体手机厂商(如小米、华为等)提供的厂商通道。

这些通道由于是手机厂商来维护的,只要手机网络可通,因此可以在我们的App在没有打开的情况下,也能把消息实时推送下去。

当然,这些第三方操作系统级别的辅助通道也存在一些问题,因此大部分情况下也只是作为一个辅助手段来提升消息的实时触达的能力,这个在后续课程中,我会再详细说明。

因此,对于消息接收通道,重点在于需要在IM服务端和接收方之间,维护一个可靠的长连接,什么叫可靠的长连接呢,这里的可靠可以理解为下列两种情况。

IM服务端和接收方能较为精确地感知这个长连接的可用性,当由于网络原因连接被中断时,能快速感知并进行重连等恢复性操作。

可靠性的另一层含义是:通过这个长连接投递的消息不能出现丢失的情况,否则会比较影响用户体验。这个问题的解决会在后续第3篇的课程中来详细展开。

我在上面大概说明了一下,逻辑上消息收发通道各自的作用和一般的实现,当然这两条通道在实际的实现上,可以是各自独立存在的,也可以合并在一条通道中。

消息未读数
现在我们有了消息的收发通道和消息的存储,用户通过发送通道把消息发到IM服务端,IM服务端对消息内容、收发双方的消息索引进行存储,同时更新双方的最近联系人的相关记录,然后IM服务端通过和消息接收方维护的接收通道,将消息实时推送给消息接收方。

如果消息接收方当前不在线,还可以通过第三方操作系统级别的辅助通道,来实时地将消息通过手机通知栏等方式推送给接收方。

整体上来看,一条消息从发送、存储、接收的生命之旅基本上比较完整了,但对于即时消息的场景来说,还有一个比较重要的功能,会对双方在互动积极性和互动频率上产生比较大的影响,这个就是消息的未读数提醒。

用过QQ、微信的用户应该都有一个比较明显的感知,很多时候为了避免通知栏骚扰,会限制掉App在通知栏提醒权限,或者并没有注意到通知栏的提醒,这些情况都可能会让我们无法及时感知到“有人给我发了新的消息”这个事情。

那么作为一个重要的补救措施就是消息的未读提醒了。就我个人而言,很多时候是看到了QQ或者微信App的角标,上面显示的多少条未读消息,才打开App,然后通过App里面具体某个联系人后面显示,和当前用户有多少条未读这个数字,来决定打开哪个联系人的聊天页进行查看。

上面通过未读提醒来查看消息的环节中涉及了两个概念:一个是我有多少条未读消息,另一个是我和某个联系人有多少条未读消息。

因此,我们在消息未读数的实现上,一般需要针对用户维度有一个总未读数的计数,针对某一个具体用户需要有一个会话维度的会话未读的计数。

那么,这两个消息未读数变更的场景是下面这样的:

张三给李四发送一条消息,IM服务端接收到这条消息后,给李四的总未读数增加1,给李四和张三的会话未读也增加1;
李四看到有一条未读消息后,打开App,查看和张三的聊天页,这时会执行未读变更,将李四和张三的会话未读减1,将李四的总未读也减1。
这个具体的未读数存储可以是在IM服务端(如QQ、微博),也可以是在接收方的本地端上存储(微信),一般来说,需要支持“消息的多终端漫游”的应用需要在IM服务端进行未读存储,不需要支持“消息的多终端漫游”可以选择本地存储即可。

对于在IM服务端存储消息未读数的分布式场景,如何保证这两个未读数的一致性也是一个比较有意思的事情,这个问题我会留到第6篇来和你详细讨论。

小结
上面我们从一条消息“产生、存储、接收”的整个生命周期出发,较为系统地从实现的角度上对消息系统的几个关键部分进行了讲述。可以简单地总结为下面几点。

消息的发送方通过发送通道来把消息提交到IM服务端。

IM服务端接收到发送的消息后,会进行消息的存储以便于后续历史消息的查看,消息的存储从实现上可以分为:消息内容存储、消息索引存储、最近联系人列表存储。

IM服务端接收到发送的消息后,还会针对接收方进行未读数的变更,以提醒用户查看未读的消息,消息未读数的实现上一般分为:用户维度的总未读和会话维度的会话未读。

IM服务端进行完消息存储和未读变更后,会通过接收通道把消息推送给接收方,接收通道一般是通过IM服务端和消息接收方之间维护的长连接来实现,还会使用第三方操作系统级别的辅助通道,来提升“自建的长连接不可用“时,实时触达的能力。

快给你的软件加IM聊天功能!相关推荐

  1. 技术扫盲 软件加壳(转)

    首先我想大家应该先明白"壳"的概念.在自然界中,我想大家对壳这东西应该都不会陌生了,植物用它来保护种子,动物用它来保护身体等等.同样,在一些计算机软件里也有一段专门负责保护软件不被 ...

  2. 设计两个程序要求用消息队列实现简单的聊天功能linux,linux软件工程师(C语言)实用教程第7章.ppt...

    第7章 进程间的通信 2 本章重点 进程通信中信号概念及信号处理进程间的管道通信编程进程间的内存共享编程 3 7 1 1信号及其使用 信号是在软件层次上对中断机制的一种模拟 是一种异步通信方式 信号可 ...

  3. php快排,网址快排 快速排名软件 thinkphp快排源码 网站快排程序 百度排名

    网址快排快速排名软件thinkphp快排源码网站快排程序百度排名 thinkphp快排源码 会员自助可以注册添加网站进行操作 根据快排系统平台的基本原理看,要做到被逐渐访问必须是该关键词已经有排名在百 ...

  4. THINKPHP聊天软件H5实时聊天室自动分配账户全开源商业源码

    介绍: THINKPHP聊天软件H5实时聊天室,自动分配账户,全开源商业源码 都是去年买的,很多买的源码基本都下架了, 源码的优点: 运营版本的聊天室,可以添加好友,建立群组,私聊,禁言功能 H5+T ...

  5. 一种NET软件加壳技术的设计与实现

    1 引言     为了保护自己的软件的技术内核不被他人轻易盗用,软件开发人员使用了各种加密技术来保障软件的版权不被侵犯,壳便是我们常用的一种软件保护手段.对于Win32 中软件加壳技术已经有非常成熟的 ...

  6. 软件加壳的简易实现方式

    软件加壳的实现方式 一 操作步骤: 未注册用户进入的界面为: 其中,机器码其实是用户电脑的MAC地址,它是唯一的且不可更改. 软件注册工具就是一个客户端小程序: 根据客户提供的MAC地址(如果是本机可 ...

  7. CTF-Crypto学习1(软件加壳、反汇编、Babe64、Rijndael密码算法)

    CTF-Crypto学习1(软件加壳.反汇编.Babe64.Rijndael密码算法) 1.软件加壳 定义: 加壳的全称应该是可执行程序资源压缩,压缩后的程序可以直接运行. 加壳的另一种常用的方式是在 ...

  8. 广达做微软服务器,微软联手广达布局云计算 称策略是软件加服务

    网易科技讯 11月6日消息,据台湾媒体报道,微软与笔记本电脑代工厂广达携手共进云端.微软资深副总裁张亚勤昨日表示,微软具先进软件及服务技术,将与台厂的信息科技(IT)硬件实力,紧密配合.云计算有三种商 ...

  9. 有什么编辑图片加文字的软件?今日推荐:图片编辑软件加文字

    小伙伴们要发朋友圈的时候,会不会喜欢在图片上面编辑文字呢?例如我们要发多个美食的图片时,会在食物上面编辑对应的名称,这样可以更好地和朋友们介绍.现在推出了越来越多的软件可以对图片进行编辑文字操作,那么 ...

最新文章

  1. 迷你MVVM框架 avalonjs 学习教程14、事件绑定
  2. 判断随机抽取代码_问卷调查:定量研究中的抽样问题(2)- 非随机抽样介绍
  3. 机器学习实战10-Artificial Neural Networks人工神经网络简介(mnist数据集)
  4. NUMA - Non Uniform Memory Architecture 非统一内存架构
  5. C++数据结构与算法(八) 队列及队列的应用
  6. 关于NLSSORT和NLS_SORT的用法
  7. Python字典的索引
  8. html中颜色打字机效果,Css打字机效果
  9. PLM 产品生命周期管理
  10. html播放iOS铃音,ios – 如何在Cordova的HTML5视频播放过程中忽略铃声设置
  11. 十六、DPM模型-颗粒流动
  12. 华为云跨云迁移工具案例实践:阿里云迁移到华为云
  13. ​什么是响应时间测试?
  14. 计算机男朋友好处,程序猿男朋友的好处
  15. 实现相册功能 java_javaWEB实现相册管理的简单功能
  16. mechanize 是 pthon 的并且可编程的 浏览器插件,
  17. 2020年Q3房产行业网络关注度分析报告
  18. 网线连接olt配置计算机IP,EPON-ONU-OLT配置手册.pdf
  19. 【算法】只有五行的Floyd最短路算法
  20. Ambari添加快速链接Quick Links

热门文章

  1. 爬虫-xpath-全国城市名称爬取
  2. Docker容器管理平台Humpback进阶-私有仓库
  3. python set() 用法
  4. Unity自定义工具之特效播放速度修改工具
  5. 2013年计算机网络原理,全国2013年4月自学考试计算机网络原理试题
  6. 硅谷年轻富翁群:挑战自我 不愿坐享其成(转)
  7. JS 模板语法 ES6
  8. Makerbase VESC 第五课 RC PPM遥控测试
  9. 李笑来python自学_李笑来登顶 GitHub TOP 榜!币圈大佬要教程序员如何自学编程
  10. HTML5期末大作业:鲜花网站设计——网上鲜花网页设计(5页) HTML+CSS+JavaScript 期末作业HTML代码...