Node.js 编程实战:即时聊天应用 —— 消息存储与推送

62 阅读4分钟

在前面的文章中,我们已经基于 WebSocket 实现了即时聊天应用的实时通信能力,并完成了群聊和私聊功能。但在真实的聊天系统中,仅有实时转发是不够的,消息存储与消息推送是两个必不可少的能力。

消息存储用于支持聊天记录查询和离线消息,而消息推送则保证用户即使暂时不在线,也不会错过重要信息。本文将围绕这两个关键能力,介绍在 Node.js 中的常见实现思路。


一、为什么需要消息存储

在最简单的聊天示例中,消息只存在于内存中,一旦用户断线或服务重启,消息就会丢失。这在真实场景中是不可接受的。

消息存储主要解决以下问题:

  • 用户可以查看历史聊天记录
  • 支持用户离线后重新上线查看消息
  • 支持多端登录时的数据同步
  • 为后续搜索和统计提供基础

因此,聊天系统必须将消息持久化存储。


二、消息数据模型设计

在开始编码之前,需要先设计消息的基本结构。

1. 消息核心字段

一条聊天消息通常包含:

  • 消息 ID
  • 发送者 ID
  • 接收者 ID 或群 ID
  • 消息类型(私聊 / 群聊)
  • 消息内容
  • 发送时间
  • 消息状态(已读 / 未读)

这些字段可以满足绝大多数聊天场景。


2. 数据库存储方案选择

常见的存储方式包括:

  • 关系型数据库(MySQL / PostgreSQL)
  • 文档数据库(MongoDB)
  • 缓存数据库(Redis,用于未读消息)

在 Node.js 项目中,常见组合是:

  • 数据库负责长期存储
  • Redis 负责临时缓存和推送辅助

三、消息入库流程设计

当服务器收到一条聊天消息时,通常执行以下流程:

  1. 校验消息合法性
  2. 写入数据库
  3. 判断接收方是否在线
  4. 在线则实时推送
  5. 不在线则标记为未读

这种设计可以保证消息不丢失。


四、消息存储实现思路

1. 私聊消息存储

私聊消息通常存储为一条独立记录,包含发送者和接收者信息。

示例逻辑:

async function savePrivateMessage(message) {
  await Message.create({
    from: message.from,
    to: message.to,
    content: message.content,
    type: 'private',
    created_at: new Date()
  });
}

数据库中可通过 from / to 字段快速查询历史消息。


2. 群聊消息存储

群聊消息通常只存一条记录,但关联群 ID。

async function saveGroupMessage(message) {
  await Message.create({
    from: message.from,
    room_id: message.roomId,
    content: message.content,
    type: 'group',
    created_at: new Date()
  });
}

查询群聊记录时,根据 room_id 过滤即可。


五、历史消息查询

为了支持聊天记录展示,通常需要提供 HTTP API 查询历史消息。

常见设计方式:

  • 私聊:根据两个用户 ID 查询
  • 群聊:根据群 ID 查询
  • 支持分页,避免一次返回大量数据

这种接口通常由 HTTP 服务提供,而不是 WebSocket。


六、离线消息处理

1. 离线消息的判定

服务器可以通过在线用户映射判断:

  • 接收方在线 → 直接推送
  • 接收方离线 → 存储为未读消息

未读状态通常需要在数据库或 Redis 中标记。


2. 离线消息推送时机

当用户重新上线时:

  1. 查询未读消息
  2. 按时间顺序推送给客户端
  3. 更新消息状态为已读

这种方式保证消息不会丢失。


七、消息推送机制设计

1. WebSocket 实时推送

对于在线用户,消息通过 WebSocket 即时推送:

  • 延迟低
  • 体验好
  • 适合聊天场景

这是聊天系统的主要推送方式。


2. 与 HTTP 接口配合

WebSocket 负责实时消息,而 HTTP 接口负责:

  • 历史消息查询
  • 未读消息统计
  • 消息状态更新

二者配合,可以让系统结构更加清晰。


八、性能与扩展性考虑

当用户规模增大后,需要考虑以下问题:

  • 消息表数据量快速增长
  • 查询性能下降
  • 单节点 WebSocket 服务压力增大

常见优化方向包括:

  • 消息表按时间或用户分表
  • 使用 Redis 缓存最近消息
  • WebSocket 服务集群化
  • 使用消息队列进行跨节点广播

这些问题在系统早期设计阶段就应有所预期。


九、总结

消息存储与推送是即时聊天系统中不可或缺的核心能力。通过合理的数据模型设计、清晰的消息流程和 WebSocket + HTTP 的协作方式,可以构建一个稳定、可扩展的聊天系统。

在《Node.js 编程实战》系列中,即时聊天项目不仅展示了 WebSocket 的使用方式,也涵盖了真实系统中常见的消息持久化和推送场景。