实时消息推送实践
实时消息推送是系统与用户实时通信的重要渠道,包括内部系统通知和外部第三方推送。
1. 推送类型
| 类型 | 说明 | 典型场景 |
|---|---|---|
| 站内通知 | 用户登录后可在站内查看 | 订单状态变更、系统公告 |
| 短信/邮件 | 运营商通道或邮件服务 | 验证码、重要通知 |
| App推送 | 极光/华为/小米通道 | 营销推送、实时提醒 |
| WebSocket | 实时双向通信 | 客服消息、实时数据大盘 |
| 钉钉/企微 | 企业内部机器人 | 告警通知、工单提醒 |
2. 设计要点
2.1 消息模型
// 消息模板
public class PushMessage {
private Long userId; // 接收用户
private String templateCode; // 模板编码
private Map<String, String> params; // 模板参数
private PushChannel channel; // 推送渠道
private LocalDateTime expiredTime; // 过期时间
}
// 推送渠道
public enum PushChannel {
站内, 短信, 邮件, APP, WEB_HOOK
}
2.2 异步发送
@Async
public void pushAsync(PushMessage message) {
// 发送前先查询用户是否订阅该类消息
if (!userSubscribed(message.getUserId(), message.getTemplateCode())) {
return;
}
// 根据渠道发送
switch (message.getChannel()) {
case 站内 -> send站内(message);
case 短信 -> send短信(message);
case APP -> sendApp(message);
case WEB_HOOK -> sendWebhook(message);
}
}
3. 实现方案
3.1 站内消息表
CREATE TABLE sys_message (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '用户ID',
title VARCHAR(200) NOT NULL COMMENT '标题',
content TEXT COMMENT '内容',
type TINYINT DEFAULT 1 COMMENT '1-系统通知 2-订单 3-活动',
read_status TINYINT DEFAULT 0 COMMENT '0-未读 1-已读',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
read_at DATETIME COMMENT '阅读时间'
) COMMENT '站内消息表';
-- 消息模板表
CREATE TABLE msg_template (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(50) UNIQUE NOT NULL COMMENT '模板编码',
name VARCHAR(100) NOT NULL COMMENT '模板名称',
content TEXT NOT NULL COMMENT '模板内容,支持占位符 {param}',
channels VARCHAR(100) COMMENT '可用渠道,逗号分隔',
status TINYINT DEFAULT 1 COMMENT '1-启用 0-禁用'
) COMMENT '消息模板表';
3.2 消息发送流程
业务触发 → 消息入库 → MQ异步投递 → 各渠道发送 → 发送记录 → 状态回执
3.3 WebHook 推送(钉钉/企微)
public void sendDingTalk(String webhookUrl, String content) {
Map<String, Object> body = Map.of(
"msgtype", "text",
"text", Map.of("content", content)
);
HttpClientUtil.post(webhookUrl, JSON.toJSONString(body),
Map.of("Content-Type", "application/json"));
}
// 企微机器人
public void sendWeCom(String webhookUrl, String content) {
Map<String, Object> body = Map.of(
"msgtype", "text",
"text", Map.of("content", content)
);
HttpClientUtil.post(webhookUrl, JSON.toJSONString(body));
}
4. 注意事项
- 频率限制:同一用户同一消息类型,5分钟内不重复推送
- 退订管理:支持用户关闭特定类型推送
- 敏感信息:手机号、银行卡号等在推送内容中脱敏
- 失败重试:MQ消费失败时,3次重试 + 死信队列告警
- 发送日志:记录发送结果,便于排查和统计送达率
5. 常见问题
| 问题 | 解决方案 |
|---|---|
| 推送延迟高 | 使用独立发送线程池,避免业务线程阻塞 |
| 重复推送 | 消息入库前查重(基于业务ID+用户ID+模板) |
| 渠道挂掉 | 降级策略:短信失败切邮件,保留站内消息 |
| 用户设备不在线 | APP推送走厂商通道(华为/小米),支持推送到达回执 |
最后更新:2026/05/11