在前面的文章中,我们已经基于 WebSocket 实现了即时聊天应用的实时通信能力,并完成了群聊和私聊功能。但在真实的聊天系统中,仅有实时转发是不够的,消息存储与消息推送是两个必不可少的能力。
消息存储用于支持聊天记录查询和离线消息,而消息推送则保证用户即使暂时不在线,也不会错过重要信息。本文将围绕这两个关键能力,介绍在 Node.js 中的常见实现思路。
一、为什么需要消息存储
在最简单的聊天示例中,消息只存在于内存中,一旦用户断线或服务重启,消息就会丢失。这在真实场景中是不可接受的。
消息存储主要解决以下问题:
因此,聊天系统必须将消息持久化存储。
二、消息数据模型设计
在开始编码之前,需要先设计消息的基本结构。
1. 消息核心字段
一条聊天消息通常包含:
这些字段可以满足绝大多数聊天场景。
2. 数据库存储方案选择
常见的存储方式包括:
- • 关系型数据库(MySQL / PostgreSQL)
在 Node.js 项目中,常见组合是:
三、消息入库流程设计
当服务器收到一条聊天消息时,通常执行以下流程:
这种设计可以保证消息不丢失。
四、消息存储实现思路
1. 私聊消息存储
私聊消息通常存储为一条独立记录,包含发送者和接收者信息。
示例逻辑:
asyncfunctionsavePrivateMessage(message) {
awaitMessage.create({
from: message.from,
to: message.to,
content: message.content,
type: 'private',
created_at: newDate()
});
}
数据库中可通过 from / to 字段快速查询历史消息。
2. 群聊消息存储
群聊消息通常只存一条记录,但关联群 ID。
asyncfunctionsaveGroupMessage(message) {
awaitMessage.create({
from: message.from,
room_id: message.roomId,
content: message.content,
type: 'group',
created_at: newDate()
});
}
查询群聊记录时,根据 room_id 过滤即可。
五、历史消息查询
为了支持聊天记录展示,通常需要提供 HTTP API 查询历史消息。
常见设计方式:
这种接口通常由 HTTP 服务提供,而不是 WebSocket。
六、离线消息处理
1. 离线消息的判定
服务器可以通过在线用户映射判断:
未读状态通常需要在数据库或 Redis 中标记。
2. 离线消息推送时机
当用户重新上线时:
这种方式保证消息不会丢失。
七、消息推送机制设计
1. WebSocket 实时推送
对于在线用户,消息通过 WebSocket 即时推送:
这是聊天系统的主要推送方式。
2. 与 HTTP 接口配合
WebSocket 负责实时消息,而 HTTP 接口负责:
二者配合,可以让系统结构更加清晰。
八、性能与扩展性考虑
当用户规模增大后,需要考虑以下问题:
常见优化方向包括:
这些问题在系统早期设计阶段就应有所预期。
九、总结
消息存储与推送是即时聊天系统中不可或缺的核心能力。通过合理的数据模型设计、清晰的消息流程和 WebSocket + HTTP 的协作方式,可以构建一个稳定、可扩展的聊天系统。
在《Node.js 编程实战》系列中,即时聊天项目不仅展示了 WebSocket 的使用方式,也涵盖了真实系统中常见的消息持久化和推送场景。