5分钟搭建企业级实时消息推送系统

前言

在现代 Web 应用中,实时消息推送是一个常见需求:

  • 社交 App 的新消息提醒
  • 电商平台的订单状态更新
  • 在线客服的实时沟通
  • 监控系统的实时告警

本文将介绍如何使用 Quick-Notify 在 5 分钟内快速搭建一个企业级的实时消息推送系统。

一、技术选型

为什么选择 Quick-Notify?

特性 Quick-Notify 自研 Socket.IO
开发周期 5 分钟 1-2 周 2-3 天
集群支持 开箱即用 需从头开发 需适配
ACK 确认 内置 需开发 需配置
消息持久化 支持 需开发 不支持

二、前置知识

2.1 基本概念

在开始之前,你需要了解以下基本概念:

概念 说明
WebSocket 全双工通信协议,服务器可主动推送
STOMP 消息协议,定义消息格式和路由规则
SockJS WebSocket 降级方案,保证浏览器兼容性
ACK 消息确认机制,确保消息可靠送达

2.2 整体架构

sequenceDiagram participant Browser as 浏览器 participant Server as 服务端 Browser->>Server: SockJS 建立 HTTP 连接 Server-->>Browser: 升级为 WebSocket Browser->>Server: STOMP CONNECT (Token) Server-->>Browser: STOMP CONNECTED Browser->>Server: STOMP SUBSCRIBE Note over Server,Browser: 实时消息推送 Server-->>Browser: MESSAGE Browser->>Server: STOMP ACK (确认收到)

2.3 环境要求

  • JDK 21+
  • Maven 3.8+
  • Redis 6+(或 Docker)

三、项目搭建

3.1 启动 Redis

bash 复制代码
# 使用 Docker 启动 Redis
docker run -d --name redis -p 6379:6379 redis

# 验证 Redis 是否启动成功
docker ps | grep redis

3.2 添加 Quick-Notify 依赖

在 Spring Boot 项目的 pom.xml 中添加:

xml 复制代码
<dependency>
    <groupId>io.stu</groupId>
    <artifactId>quick-notify-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3.3 配置 application.yml

yaml 复制代码
spring:
  application:
    name: my-app
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:quicknotify;MODE=MySQL;DB_CLOSE_DELAY=-1
    username: sa
    password:
  sql:
    init:
      mode: always

redisson:
  single-server-config:
    address: redis://127.0.0.1:6379

server:
  port: 8080

3.4 启动类

java 复制代码
@SpringBootApplication
public class MyAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }
}

注意 :Quick-Notify 使用 Starter 模式,无需添加 @ComponentScan,自动配置会自动生效。

四、后端发送消息

4.1 注入 NotifyManager

java 复制代码
@Service
public class OrderService {

    @Autowired
    private NotifyManager notifyManager;

    public void sendOrderNotification(String userId, String orderId, String status) {
        // 构建消息
        Map<String, Object> data = new HashMap<>();
        data.put("orderId", orderId);
        data.put("status", status);
        data.put("message", "您的订单已" + status);

        // 发送通知
        NotifyMessageLog message = NotifyMessageLog.builder()
            .receiver(userId)
            .type("ORDER_STATUS")
            .data(data)
            .viewed(false)
            .build();

        notifyManager.saveAndPublish(message);
    }
}

4.2 REST API 发送(测试用)

java 复制代码
@RestController
public class TestController {

    @Autowired
    private NotifyManager notifyManager;

    @PostMapping("/notify/{userId}")
    public String notifyUser(@PathVariable String userId, @RequestBody String message) {
        NotifyMessageLog msg = NotifyMessageLog.builder()
            .receiver(userId)
            .type("STRING_MSG")
            .data(message)
            .build();

        notifyManager.saveAndPublish(msg);
        return "消息已发送";
    }
}

五、前端集成

5.1 引入依赖

在 HTML 中引入 SockJS 和 STOMP 客户端库:

html 复制代码
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.6.1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.0/lib/stomp.umd.min.js"></script>

5.2 WebSocket 连接与订阅

连接流程说明:

  1. 创建客户端:使用 SockJS 封装 WebSocket 连接
  2. 携带 Token 连接 :在 connect() 中传入 Authorization header 进行认证
  3. 订阅消息 :在连接成功的回调函数中订阅 /user/queue/msg
  4. ACK 确认:收到消息后发送 ACK 确认

关键代码:

javascript 复制代码
// Step 1: 创建 STOMP 客户端
const stompClient = Stomp.over(new SockJS('http://localhost:8080/stomp-ws'));

// Step 2: 连接 WebSocket
// dev 模式:token 直接作为用户ID(如 'testuser')
// 生产环境:请替换为真实的 JWT Token
stompClient.connect(
    { Authorization: 'testuser' },

    // Step 3: 连接成功回调 - 必须在此订阅消息
    function(frame) {
        console.log('连接成功');

        // 订阅个人消息队列
        stompClient.subscribe('/user/queue/msg', function(message) {
            const data = JSON.parse(message.body);
            console.log('收到消息:', data);

            // Step 4: 发送 ACK 确认
            stompClient.send('/app/ack', {}, data.id);
        });
    },

    // 连接失败回调
    function(error) {
        console.error('连接失败:', error);
    }
);

注意事项:

  • Token 认证:CONNECT 帧通过 Authorization header 携带用户标识
  • 订阅时机:必须在 connect() 成功回调中订阅,不能在外部订阅
  • 消息路由:订阅 /user/queue/msg 会自动路由到 /user/{userId}/queue/msg
  • dev 模式:token 直接作为用户ID;生产环境需继承 StompWebsocketInterceptor 重写 extractUserId() 实现 JWT 解析

5.3 完整示例页面

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>Quick-Notify Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.6.1/dist/sockjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.0/lib/stomp.umd.min.js"></script>
</head>
<body>
    <h1>Quick-Notify 演示</h1>
    <div id="messages"></div>
    <button onclick="sendMessage()">发送测试消息</button>

    <script>
        const stompClient = Stomp.over(new SockJS('http://localhost:8080/stomp-ws'));

        stompClient.connect(
            { Authorization: 'testuser' },
            function() {
                console.log('连接成功,开始订阅消息');

                stompClient.subscribe('/user/queue/msg', function(message) {
                    const data = JSON.parse(message.body);
                    document.getElementById('messages').innerHTML +=
                        '<p>收到消息: ' + data.data + '</p>';

                    // 发送 ACK 确认
                    stompClient.send('/app/ack', {}, data.id);
                });
            }
        );

        function sendMessage() {
            fetch('/notify/testuser', {
                method: 'POST',
                body: 'Hello World!'
            });
        }
    </script>
</body>
</html>

六、测试验证

6.1 启动应用

bash 复制代码
mvn spring-boot:run

6.2 发送测试消息

bash 复制代码
# 发送消息给 testuser
curl -X POST -d "Hello from curl!" http://localhost:8080/notify/testuser

6.3 预期效果

  1. WebSocket 连接成功(浏览器控制台显示 "连接成功")
  2. 收到消息推送(页面显示消息内容)
  3. ACK 确认日志(服务端显示 "确认成功")

七、常见问题

Q1: 启动报错 "Port 8080 was already in use"

bash 复制代码
# 查找占用端口的进程
lsof -i :8080

# 终止进程
kill -9 <PID>

Q2: 消息发送成功但客户端收不到

  1. 检查客户端是否正确连接到 /stomp-ws 端点
  2. 确认 Token 认证通过
  3. 查看服务端日志是否有 [ACK-REDIS] 相关输出

Q3: Redis 连接失败

bash 复制代码
# 确认 Redis 已启动
docker ps | grep redis

# 如果没有运行,启动 Redis
docker run -d --name redis -p 6379:6379 redis

八、总结

本文介绍了如何使用 Quick-Notify Spring Boot Starter 快速搭建实时消息推送系统:

  • 5 分钟即可接入
  • 一行依赖,零配置
  • 支持集群部署
  • ACK 消息确认
  • 消息持久化

完整源码请参考 quick-notify-example 模块。

相关推荐
Thneonl1 小时前
Claude Code 频繁 Compacting?发布次日我根治了 2 个痛点
后端·rust
环境栈笔记3 小时前
指纹浏览器安卓怎么用?从运行位置到功能验收,先把三种方案分清
前端·人工智能·后端·自动化
小小张说故事3 小时前
Python 的 list 为什么能装下任何类型?拆开 PyObject* 数组,以及它让你多付了多少内存
后端·python
小蒜学长3 小时前
基于SpringBoot 框架的自媒体新闻系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·mysql数据库·自媒体新闻管理系统
IT_陈寒4 小时前
JavaScript实战技巧总结:深挖数组遍历的性能陷阱与实战解法
前端·人工智能·后端
学长毕设14 小时前
基于springboot的食品仓库管理系统
java·spring boot·后端
滴滴答答哒14 小时前
ASP.NET Core + FaceRecognitionDotNet 人脸识别实战:DI 单例、特征向量持久化与 1:N 比对
后端·asp.net
Da Da 泓15 小时前
五子棋 Web 端测试用例报告
前端·网络·websocket·网络协议·测试用例·项目
captain37616 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
IT_陈寒18 小时前
Java序列化踩坑记:对象循环引用让我加班两小时
前端·人工智能·后端