消息推送服务-离线消息处理

在上一篇文章,小编浅谈了即时通讯消息的投递机制,但有人会问,如果用户不在线的时候,消息应该要怎么处理呢?现在小编就来谈谈,如果用户不在线时,如何保证消息的不丢失。

 

即时通讯消息的可靠投递

我们来看看下边的流程:

即时通讯消息的可靠投递

这是即时通讯消息的可靠投递流程,这里边没有考虑到的一种情况就是,如果用户不在线的时候,这个消息要如何处理呢?于是,我们需要引入离线消息储存机制。

 

离线消息储存机制

我们先来看看我们改进后的流程

消息可靠投递+离线消息存储

图中便是我们整个消息机制的流程:

 

用户在线:

  1. 用户A向 Im-server 发送了一个消息请求包,即 Msg:R
  2. Im-server 成功处理之后,给用户A一个消息响应包,即 Msg:A
  3. Im-server 去 User-status-cache 缓存中获取用户B的在线情况
  4. User-status-cache 缓存告诉 Im-server 用户在线
  5. Im-server 向用户B推送一个消息通知包,即 Msg:N
  6. 用户B接收成功之后,向 Im-server 发送一个消息请求包,即 Ack:R
  7. Im-server 接收成功之后,给用户B发送一个消息响应包:即 Ack:A
  8. Im-server 最后给用户A发送一个消息通知包:即 Ack:N

 

用户不在线:

  1. 用户A向 Im-server 发送了一个消息请求包,即 Msg:R
  2. Im-server 成功处理之后,给用户A一个消息响应包,即 Msg:A
  3. Im-server 去 User-status-cache 缓存层中获取用户B的在线情况
  4. User-status-cache 缓存层告诉消息服务器用户不在线
  5. Im-server 向 Im-Db 数据层发送一条保存消息请求
  6. Im-Db 数据层 成功保存消息后,给 Im-server 发送一条保存成功通知
  7. 随后 Im-server 给用户A发送一个用户B已收到消息的通知包:即 Ack:N

引入离线消息储存,就能解决用户不在线的时候,保证消息不丢失的问题。

 

问题

用户B 登录之后,如何获取离线消息呢?

 

离线消息的获取

首先我们来看看小编的离线消息表:

// 离线消息表(根据个人业务创建)

CREATE TABLE `offline_message` (
  `id` int(1) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `msg_id` varchar(32) DEFAULT NULL COMMENT '消息标识(发送人随机生成,不唯一)',
  `send_uid` int(1) NOT NULL COMMENT '发送人ID',
  `received_uid` int(1) NOT NULL COMMENT '接收人ID',
  `msg_type` tinyint(1) NOT NULL COMMENT '消息类型',
  `msg_content` varchar(500) NOT NULL COMMENT '消息内容',
  `send_time` int(1) NOT NULL COMMENT '发送时间',
  `received_time` int(1) NOT NULL COMMENT '成功接收时间',
  `is_receive` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否接收,默认0否,1是',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='离线消息表';

以上就是我们的离线消息表了,这样,我们就可以在用户上线之后,拉取用户的离线消息了。

拉取离线消息

  1. 用户B向 server 发送一个拉取数据的请求
  2. server 接收到请求后向 Im-DB 发送拉取数据请求
  3. Im-DB 接收到请求后,查询数据并返回给 server
  4. server 拿到数据后,发送给用户B
  5. 用户B成功处理好消息之后,给 server 发送接收成功请求
  6. server 接收到成功请求之后,给 Im-DB 发送删除离线数据请求
  7. Im-DB 接收到请求后,删除数据

 

上述的流程中,如果用户B有多个用户(或者群)的离线消息,则每次拉取都会循环拉取大量信息,而这些信息中,部分信息用户B可能不会去查看,这就造成了极大的性能浪费,如:

<?php 

// 所有消息记录
$messages = array();

// 临时记录
$message = array();

foreach ($friend_ids as $value) {
    $message['uid'] = $value;
    $message['list'] = get_friend_message($value);
    // 放入所有消息记录数组中
    $messages[] = $message;
}

?>

 

优化方案

由于用户是只有点击好友聊天之后,才会看到该好友的聊天记录,所以,我们可以先获取每个好友的消息数量,我们可以做以下改进:

用户登录,拉取离线消息

用户点击好友聊天之后,再来取该好友的离线消息:

获取指定用户的离线消息

我们将获取离线消息的步骤,改进之后,就可以大大的减少我们服务器不必要的消耗。但这是不是就是最完美的方案呢?小编认为不是,因为以上步骤中,如果第5步成功接收的数据包丢失之后,Im-DB 中该不会删除用户已经获取的消息,这就造成数据多余,所以我们还需要进一步的改进:

获取指定用户的离线消息(增加删除确认消息)

  1. 当用户B成功接收消息后,向 server 发送成功接收请求包,并将改请求放入队列中
  2. server 收到请求后,向 Im-DB 发送删除数据请求包
  3. Im-DB 收到请求后,删除数据,并向 server 发送删除成功请求包
  4. server 接收到请求后,向用户B发送删除成功请求包
  5. 用户B将这次的请求从队列中移除(del_id来判断)
  6. 如果用户B在一定时间内没有收到删除成功请求,则从队列中取出相应的请求包,发送给 server,直至收到删除成功的请求包为止

以上就是小编觉得比较完整的离线消息储存读取机制的流程了。

 

结论

  1. 增加离线消息储存表,确保离线消息不丢失
  2. 先拉取用户离线消息的数量,再根据需要拉取用户的详细离线消息,降低服务器消耗
  3. 增加删除确认机制,确保离线消息成功删除,避免出现多余的消息
posted @ 2023-02-23 13:31  CNHK19  阅读(143)  评论(0编辑  收藏  举报