Java实时聊天系统需明确“谁发给谁”“消息怎么存”“掉线怎么办”:用Spring Boot+WebSocket,MySQL存消息,Redis管在线状态;连接带userId标识,存入Redis Hash;消息统一JSON格式含type/msgId;实时推送+HTTP拉取历史;主动心跳+定时任务管理上下线状态。

Java 项目中用 WebSocket 实现网页实时聊天,关键不是堆砌技术,而是理清“谁发给谁”“消息怎么存”“掉线怎么办”这三件事。Spring Boot + WebSocket 是最稳妥的组合,配合 MySQL 存消息、Redis 管在线状态,就能支撑起一个生产可用的聊天系统。
WebSocket 连接要带身份,不能裸连
用户登录后,前端必须把唯一标识(比如 userId 或 token)带上,才能建立带上下文的连接。常见做法是把用户 ID 嵌入 WebSocket 路径,例如 wss://api.example.com/ws/1001,后端用 @ServerEndpoint("/ws/{userId}") 拦截并提取。
- 服务端在
@OnOpen方法里,把当前 Session 和 userId 关联起来,存进 ConcurrentHashMap 或 Redis 中,供后续广播或定向发送使用 - 避免用全局静态 Map 存 Session —— 容器重启就丢,集群下也不生效;推荐用 Redis 的 Hash 结构存
online_users:{userId} → sessionId - 连接建立时顺便推送“欢迎消息”和“未读会话数”,提升首屏体验
消息格式要统一,前后端约定好字段
别传裸字符串,定义一个标准 JSON 消息体,比如:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
{
"type": "chat",
"from": "1001",
"to": "1002",
"content": "你好",
"timestamp": 1722212345678,
"msgId": "msg_abc123"
}
-
type区分私聊、群聊、系统通知等场景,方便前端路由处理 -
msgId由服务端生成(如 Snowflake ID),用于去重、幂等和历史消息回溯 - 服务端收到消息后,先校验 from/to 合法性、内容长度、敏感词,再落库(MySQL 插入 message 表),最后转发
消息既要实时推,也要能查历史
WebSocket 负责“推”,REST API 负责“拉”。这是主流设计,不矛盾。
立即学习“Java免费学习笔记(深入)”;
- 实时消息走 WebSocket:服务端收到 A 发给 B 的消息,查出 B 当前是否在线(查 Redis),在线则直接
session.getBasicRemote().sendText(...)推送;离线则只写库,不推送 - 历史消息走 HTTP:提供
GET /api/messages?chatId=1001_1002&page=1&size=20接口,返回已持久化的消息列表(含时间、已读状态等) - 消息表至少包含字段:
id, from_id, to_id, content, msg_type, create_time, status(0-发送中/1-已送达/2-已读)
上线/下线状态得可感知,别靠心跳硬扛
单纯依赖 WebSocket 的 @OnClose 不可靠(网络闪断、浏览器崩溃不会触发)。需要主动管理状态。
- 前端每 30 秒发一次 ping 消息(类型为
ping),服务端收到后立即回pong,同时更新 Redis 中该用户的 last_active_time - 后台起一个定时任务(如每分钟扫描),把
last_active_time超过 90 秒的用户标记为“离线” - 用户上线时,先调 REST 接口
POST /api/user/online更新状态,再建 WebSocket 连接;下线时主动调POST /api/user/offline,再关闭连接

















