功能需求分析
实现一个消息中心,支持消息推送,并在有新消息时更新用户的小红点数
- 功能需求
- 支持实时推送
- 支持小红点数量递增
- 支持多用户,多维度展示小红点
- 小红点状态的持久化(防止服务重启丢失)
- 支持高并发与海量用户场景
- 非功能需求
- 高性能:保证推送消息的低延迟
- 高可用性:消息推送的小红点数据更新不能丢失
- 可扩展性:支持用户增长和未来功能扩展
设计思路
- 核心流程
消息的生
- 消息中心接收系统的业务事件(新消息的产生)
更新小红点的数量:
- 更新小红点数据,记录用户有未读消息
推送消息:
- 将消息实时推送给用户
用户读取操作:
- 用户查看消息后,小红点数重置为0或者其他特殊状态
- 系统架构
核心组件和模块
模块 | 描述 | 实现框架或组件 |
---|---|---|
消息API | 提供消息查询,消息发送等接口 | Java API |
消息队列 | 用于异步解耦消息的生成,推送和存储 | Kafka、RocketMQ |
缓存 | 用户快速存储用户小红点计数 | Redis |
推送服务 | 将新消息实时推送到用户设备 | WebSocket、MQTT、APNs/FCM| |
数据库 | 持久化消息内容和小红点数据 | MySQL、MongDB |
- 逻辑设计
消息的接收与处理
- 消息中心接收新消息(通过Rest API、事件流等)
- 数据消息写入数据库,使用消息队列异步处理
小红点计数更新
小红点计数存储在Redis中,按照用户纬度维护
Key: unread_count:user_id Value: { message_type: count }
每次新消息产生时,Redis的计数加1
redis.hincrBy("unread_count:" + userId, messageType, 1);
实时推送
- 通过WebSocket或者消息队列通客户端有消息
- 推送内容包括小红点更新和消息预览内容
用户读取消息
- 用户点击点击消息以后,标记消息为已读
小红点计数清零以后,更新Redis和数据库
redis.hset("unread_count:" + userId, messageType, 0);
选型和框架
消息队列
Kafka:
高吞吐量,适合日志型消息推送 消息分区支持高并发
RabbitMQ:
支持丰富的路由规则,适合复杂的推送需求
RocketMQ:
天然支持事务消息和延迟消息,适合高可靠推送
缓存
Redis:
使用哈希表存储小红点计数,支持高效读写 提供过期策略,可以清理长期未读数据
推送技术
WebSocket:
实时双向通信,适合网页和移动端
MQTT:
轻量级协议,适合物联网和高并发场景
APNs/FCM:
苹果和谷歌的推送服务,适合移动设备通知
数据库
MySQL:
存储消息内容,适合结构化查询。
MongoDB
存储消息内容,支持灵活的文档型存储和查询
服务框架
SpringBoot
快速构建 REST API和服务逻辑
Netty
实现高性能 WebSocket服务
Spring WebFlux
支持非阻塞式的异步推送
示例代码
- 消息生产与小红点更新
@RestController
@RequestMapping("/message")
public Class MessageController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@PostMapping("/send")
public ResponseEntity<String> sendMessage(@RequestBody Message message) {
// 1. 存储消息到数据库
message.saveMessage(message);
// 2. 更新 Redis 中的小红点计数
String key = “unread_count:” + message.getUserId();
redisTemplate.opsForHash().increment(key, message.getType(), 1);
// 1和2追求接口性能的话可以做成异步
// 3.推送消息
notificationService.pushMessage(message);
// 4.异步接收推送消息返回的数据
return ReponseEntity.ok(“messgae ok”);
}
}
- 小红点查询
@RestController
@RequestMapping("/notification")
public class NotificationController {
@GetMapping("/unread")
public Map<Object, Object> getUnreadCount(@RequestParam String userId) {
String key = "unread_count:" + userId;
return redisTemplate.opsForHash().entries(key);
}
}
- 用户读取消息
@RestController
@RequestMapping("/message")
public class ReadMessageController {
@Autowired
private RedisTemplate<Object,String> redisTemplate;
@PostMapping("/read")
public ResponseEntity<String> readMessage(@RequestParam String userId, @RequestParam String type) {
// 1. 标记消息为已读(更新数据库)
messageService.markMessagesAsRead(userId, type);
// 2. 清零小红点计数
String key = “unread_count:” + userId;
redisTemplate.opsForHash().put(key, type, 0);
return ResponseEntity.ok("Messages marked as read");
}
}
扩展功能
消息类型区分:
- 例如分为系统消息、订单消息、私信等,每种类型独立计数。
延迟推送:
- 使用消息队列支持消息的延迟发送。
分组推送:
- 支持按用户群体(如订阅相同主题)推送。
过期清理:
- 定期清理 Redis 中长期未读的小红点计数。
未完待续。。。。。。
思路补充
IM系统和消息中心解耦逻辑
- IM和消息中心解体,IM只是消息中心的使用者
消息实体内容
- 消息体
- 消息未读数
- 消息调用者
- 消息通知方式
- 消息发送连接方式(短链接/长链接)
消息推送客户端的感知
- APP外推送
- APP内推送
- 短信
- 站内通知
- 私信
特殊业务推送
- 1设备n用户推送(APP双启)
- n设备单用户推送 -> 该用户最近最新使用设备
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。