IM通信——小程序聊天

2,339 阅读7分钟

引子

先考虑几个场景:

  1. A同时给你发了2条消息,怎么处理这些消息
  2. 你的好友非常多,1s中内收到了100条消息,怎么高效率处理消息。
  3. 你长时间未登录,登录一瞬间收到了1000条消息,怎么处理批量消息。
  4. 你同时登录了PC端,手机端,小程序端,A这时候给你发了1条消息,3端怎么处理这条消息
  5. 你长时间未登录PC端,但是经常登录手机端,再登录PC端时如何同步历史消息
  6. 你的网络很差,虽然一直在线,怎么保证不漏消息

背景

公司有一款交友app,允许用户在APP内聊天。现在拓展业务到微信小程序,使微信小程序和APP打通。而作为前端切图仔的我,自然是要开发这款小程序了。

正文

注意:每个业务场景不一样,不尽相同。下面所描述的仅仅是我们所采用的方案。

一、消息记录怎么存储

数据库怎么存储消息记录

  1. n个用户就有n个消息记录表(实际上会按时间分表),只存储该用户收到的消息,按时间排序
  2. 每个用户均记录一个【消息标识位】,用于区分未读和已读的记录

前端怎么存储消息记录

背景:小程序有限制:最多存储10M数据,每个key最多存储1M数据

前端单个用户消息的记录,一个存储的key,且最多存储3000条消息记录。

二、消息标识位

当A用户发送消息M,后端服务发送一条单播到B用户,B用户利用消息标识位请求该新消息。而标识位可以存储在本地或服务端。
方案1:消息标识为服务端存储

img.png 方案2:消息标志位本地存储

img_1.png

使用本地存储还是服务端存储标志位?

  1. 若标识位存储在本地,则多端(app端、小程序端)消息拉取互不干扰,但是同一条消息会被多端重复收取。若本地首次消息未有消息标志位,需要提前请求消息标识位存储到本地。
  2. 若消息存储在服务端,当一端收取1条消息,并将消息标志位由K更新为K+1后。同时另一端请求消息标识位,则可能返回K或K+1。导致多端同时在线时,消息接收有异常。

三、消息去重?

  1. 什么情况下,消息会重复?

基于上面的描述,当接受到新消息,后端服务会通知客户端。客户端利用标志位去拉取最新的消息。

考虑到以下这种情况:A同时给B发送了2条消息,B收到两条新消息的单播。由于B收到了2次单播,故去拉取2次新消息,每次拉取的消息都是2条,就一共拉取了4条消息。

img_2.png 2. 怎么去重?

方案1:利用每条消息的uuid,去重最近100条数据和最近1分钟内的消息。

算法省略

方案2:互斥锁机制:

互斥锁保证了同一时间只有一个线程对资源进行处理

我们把 【1.收到并解析新消息单播通知;2.请求标识位;3.拉取增量消息;4.更新标识位 】 这4步当做一个线程。当此线程未处理完成,不处理新消息。则避免的重复处理消息。 img_3.png 上述两种方案各有优缺点,方案1逻辑清晰简单,但有性能上的担忧。方案2直接不处理段时间内的重复消息提示,性能负担小。但是方案2可能会漏收消息。当然2种方式可以结合使用

四、消息漏收?

  1. 网络问题漏收
    如果A给B发了一条消息,此时B网络差,未收到单播通知,则会漏掉消息,解决办法就是定时拉取增量消息。
  2. 互斥锁逻辑导致漏收
    A给B发送了一条消息,B去拉取此消息,在B请求或解析消息的同时,A又发了一条新消息,由于B此时处于锁状态,不处理此新消息,导致漏收。
    有方案:当发现有消息被锁拦截后,做一个状态标记。当解析完成当前线程后再去拉取被锁的消息。
    下图简单描述一下状态:
# 0表示被锁标识,1表示解锁标识,*表示正在请求解析消息

# 消息1:01***************                        [第一次处理消息]                   
# 消息2:       0                                 [被锁不处理,状态设为0]  
# 消息2:                   1***************       [消息1处理完后,发现状态是0,继续处理消息2]

五、大量消息怎么处理

大量未读消息怎么处理?

若有大量未读消息,则采取分页处理。后端返回消息列表的同时携带一个字段表示「消息是否已拉取完整」。若未拉取完整,则再拉取解析完再继续拉取一次。

大量高频率的消息怎么解析并渲染到页面上?

利用节流机制:由于消息的渲染需要占用性能,使用节流处理消息,有新消息解析完先等一等再渲染到页面上。
而笔者使用的是防抖机制渲染,因为实际情况下并不会有高频消息。且笔者使用的发布订阅模式,同个消息会发布多次更新通知,如已读未读状态变更通知,新消息通知,消息列表变化通知, 只需极短防抖间隔即可。

六、回复开头的问题

回答最开始的6个问题。

  1. A同时给你发了2条消息,怎么处理这些消息
  • 答:互斥锁或利用uuid去重
  1. 你的好友非常多,1s中内收到了100条消息,怎么高效率处理消息。
  • 答:节流处理
  1. 你长时间未登录,登录一瞬间收到了1000条消息,怎么处理批量消息。
  • 答:分页
  1. 你同时登录了PC端,手机端,小程序端,A这时候给你发了1条消息,3端怎么处理这条消息
  • 答:消息标志位存本地,则互不干扰。【如果后端服务区分不同的端,则能更灵活】
  1. 你长时间未登录PC端,但是经常登录手机端,再登录PC端时如何同步历史消息
  • 答:消息标识位存本地,根据消息标志位拉取消息。
  1. 你的网络很差,虽然一直在线,怎么保证不漏消息
  • 答:定时拉取消息

七、微信小程序与浏览器差异

  1. 进入后台一段时间会挂起,进程被暂停。实际上未发现严重bug,只是在后台收不到消息,重新进入小程序再拉取一次消息即可。
  2. 本地存储10M限制和单个key1M限制。导致需要有机制删除旧消息。
  3. 不支持js的动态解析语法,进而导致不正常protobufjs的反射解析模式,只能支持静态解析模式。
  4. 小程序有包的体积限制,如protobufjs会生成大量的静态语法,且无法进行tree-shaking,导致主包很容易超过2M限制。
  5. 小程序性能差,不同的手机体验差距较大,如有的手机需要等2s的白屏才能进入IM页面。
  6. bug性问题:如uuid兼容性问题、小程序对x instanceof ArrayBuffer判断失败。
  7. 兼容问题:window、cookie、new Function、eval 等。