8.8 KiB
8.8 KiB
离线消息功能实现说明
📋 功能概述
离线消息服务是直播IM系统的核心功能之一,用于在用户离线时保存消息,并在用户重新上线时推送给用户。
🎯 核心功能
1. 消息保存
- 当用户离线时,系统自动将发送给该用户的消息保存到Redis队列中
- 每个用户最多保存100条离线消息(可配置)
- 离线消息保存7天后自动过期(可配置)
2. 消息推送
- 用户上线时,系统自动推送所有离线消息
- 推送完成后自动清除已推送的离线消息
3. 消息管理
- 获取离线消息数量
- 获取离线消息列表(支持分页)
- 删除指定数量的离线消息
- 清空所有离线消息
🏗️ 技术架构
数据存储
使用Redis List存储离线消息:
Key格式: offline:msg:{userId}
数据结构: List
过期时间: 7天
最大长度: 100条
核心类说明
1. OfflineMessageService (接口)
定义离线消息服务的所有方法:
saveOfflineMessage()- 保存离线消息getOfflineMessages()- 获取离线消息(分页)getAllOfflineMessages()- 获取所有离线消息clearOfflineMessages()- 清空离线消息getOfflineMessageCount()- 获取消息数量removeOfflineMessages()- 删除指定数量的消息
2. OfflineMessageServiceImpl (实现类)
实现离线消息服务的具体逻辑:
- 使用RedisUtil操作Redis
- 自动添加时间戳
- 限制消息数量
- 设置过期时间
3. OfflineMessageController (控制器)
提供HTTP接口:
GET /api/front/offline-messages/count/{userId}- 获取消息数量GET /api/front/offline-messages/list/{userId}- 获取消息列表GET /api/front/offline-messages/all/{userId}- 获取所有消息DELETE /api/front/offline-messages/clear/{userId}- 清空消息DELETE /api/front/offline-messages/remove/{userId}- 删除指定数量POST /api/front/offline-messages/save- 保存消息(测试接口)
4. OfflineMessageCleanupTask (定时任务)
定期清理和监控离线消息:
- 每天凌晨3点统计离线消息使用情况
- 每小时检查离线消息健康状况
- 发现异常大的消息队列时发出告警
🔄 工作流程
消息发送流程
1. 用户A发送消息给用户B
↓
2. 检查用户B是否在线
↓
3. 如果在线 → 直接推送消息
↓
4. 如果离线 → 保存到离线消息队列
↓
5. 用户B上线时 → 自动推送离线消息
↓
6. 推送完成 → 清除已推送的消息
WebSocket集成
在PrivateChatHandler中已经集成了离线消息功能:
@Override
public void afterConnectionEstablished(WebSocketSession session) {
// ... 连接建立逻辑 ...
// 推送离线消息
List<String> offlineMessages = offlineMessageService.getAllOfflineMessages(userId);
if (!offlineMessages.isEmpty()) {
for (String offlineMsg : offlineMessages) {
sendToSession(session, offlineMsg);
}
// 清除已推送的离线消息
offlineMessageService.clearOfflineMessages(userId);
}
}
在消息发送时检查用户是否在线:
// 检查对方是否在线
if (onlineStatusService.isUserOnline(otherUserId)) {
// 在线则直接推送
notifyUser(otherUserId, buildNewMessageNotification(conversationId, response));
} else {
// 离线则保存为离线消息
offlineMessageService.saveOfflineMessage(otherUserId, chatMsg);
}
📊 Redis数据结构
离线消息队列
# Key格式
offline:msg:{userId}
# 数据类型
List
# 示例数据
LRANGE offline:msg:1 0 -1
1) "{\"type\":\"chat\",\"content\":\"你好\",\"timestamp\":1234567890,\"savedAt\":1234567900}"
2) "{\"type\":\"chat\",\"content\":\"在吗\",\"timestamp\":1234567891,\"savedAt\":1234567901}"
# 过期时间
TTL offline:msg:1
604800 # 7天 = 7 * 24 * 3600秒
消息格式
{
"type": "chat",
"messageId": "msg_123456",
"userId": 2,
"username": "张三",
"avatarUrl": "http://example.com/avatar.jpg",
"message": "你好,在吗?",
"timestamp": 1234567890,
"savedAt": 1234567900,
"status": "sent"
}
🔧 配置说明
可配置参数
在OfflineMessageServiceImpl中:
// 离线消息最大保存数量(每个用户)
private static final int MAX_OFFLINE_MESSAGES = 100;
// 离线消息过期时间(秒)- 7天
private static final long OFFLINE_MESSAGE_EXPIRE_SECONDS = 7 * 24 * 3600;
修改配置
如果需要修改配置,可以:
- 直接修改常量值
- 或者改为从配置文件读取:
@Value("${offline.message.max-count:100}")
private int maxOfflineMessages;
@Value("${offline.message.expire-seconds:604800}")
private long offlineMessageExpireSeconds;
然后在application.yml中配置:
offline:
message:
max-count: 100 # 最大保存数量
expire-seconds: 604800 # 过期时间(7天)
🧪 测试方法
1. 使用测试HTML页面
打开测试离线消息功能.html文件,可以测试:
- 保存离线消息
- 获取离线消息
- 删除离线消息
- 清空离线消息
2. 使用Postman测试
保存离线消息
POST http://localhost:8081/api/front/offline-messages/save?userId=1
Content-Type: application/json
{
"type": "chat",
"content": "测试消息",
"timestamp": 1234567890
}
获取消息数量
GET http://localhost:8081/api/front/offline-messages/count/1
获取消息列表
GET http://localhost:8081/api/front/offline-messages/list/1?limit=50
获取所有消息
GET http://localhost:8081/api/front/offline-messages/all/1
删除指定数量
DELETE http://localhost:8081/api/front/offline-messages/remove/1?count=5
清空所有消息
DELETE http://localhost:8081/api/front/offline-messages/clear/1
3. 使用Redis CLI测试
# 查看用户1的离线消息
redis-cli LRANGE offline:msg:1 0 -1
# 查看消息数量
redis-cli LLEN offline:msg:1
# 查看过期时间
redis-cli TTL offline:msg:1
# 手动添加消息
redis-cli LPUSH offline:msg:1 '{"type":"chat","content":"测试"}'
# 手动删除消息
redis-cli DEL offline:msg:1
📈 性能优化
1. 批量操作
- 使用Redis Pipeline批量获取多个用户的离线消息
- 减少网络往返次数
2. 消息限制
- 限制每个用户最多100条离线消息
- 超过限制时自动删除最旧的消息
- 避免内存占用过大
3. 过期策略
- 设置7天过期时间
- Redis自动清理过期数据
- 减少手动清理的开销
4. 异步推送
- 用户上线时异步推送离线消息
- 不阻塞WebSocket连接建立
- 提高用户体验
🔒 安全考虑
1. 权限验证
- 只能获取自己的离线消息
- 需要在Controller中添加Token验证
- 防止越权访问
2. 消息加密
- 敏感消息可以加密存储
- 推送时解密
- 保护用户隐私
3. 限流保护
- 限制API调用频率
- 防止恶意刷接口
- 保护系统稳定性
🐛 常见问题
1. 离线消息没有推送?
可能原因:
- 用户没有真正离线(还有其他设备在线)
- WebSocket连接建立失败
- Redis连接异常
解决方法:
- 检查在线状态服务
- 查看WebSocket连接日志
- 检查Redis连接
2. 离线消息丢失?
可能原因:
- Redis数据过期
- 消息超过100条被删除
- Redis重启导致数据丢失
解决方法:
- 增加过期时间
- 增加最大消息数量
- 启用Redis持久化(AOF/RDB)
3. 离线消息重复推送?
可能原因:
- 清除消息失败
- 用户多次连接
- 并发问题
解决方法:
- 添加消息去重逻辑
- 使用分布式锁
- 检查清除逻辑
📝 后续优化建议
1. 消息持久化
- 将重要消息同时保存到MySQL
- Redis作为缓存,MySQL作为持久化存储
- 提高数据可靠性
2. 消息优先级
- 支持消息优先级
- 重要消息优先推送
- 普通消息延迟推送
3. 消息分类
- 支持不同类型的离线消息
- 文本消息、图片消息、系统通知等
- 分别处理和展示
4. 消息统计
- 统计离线消息的发送量
- 分析用户活跃度
- 优化推送策略
5. 消息推送优化
- 支持增量推送
- 避免一次性推送大量消息
- 提高用户体验
📚 相关文档
🎉 总结
离线消息功能已经完整实现,包括:
✅ 消息保存和推送 ✅ HTTP接口管理 ✅ WebSocket集成 ✅ 定时清理任务 ✅ 测试页面 ✅ 完整文档
可以开始测试和使用了!
文档版本: v1.0
最后更新: 2024-12-25
作者: Kiro AI Assistant