java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot WebSocket实时消息推送

SpringBoot+WebSocket实现实时消息推送系统(在线聊天/通知)

作者:张老师技术栈

本文详细介绍了如何使用SpringBoot整合WebSocket实现服务端主动推送消息,覆盖了从零搭建实时消息推送系统的核心步骤;包括引入依赖、配置WebSocket、编写WebSocketServer以及业务代码调用推送等关键环节,需要的朋友可以参考下

WebSocket 是实现服务端主动推送消息的常用技术,适用于在线聊天、系统通知、实时数据展示等场景。本文从零搭建一个 SpringBoot + WebSocket 的实时消息推送系统。

一、WebSocket 是什么

传统 HTTP 通信只能由客户端发起请求,服务端被动响应。WebSocket 建立连接后,服务端可以主动向客户端推送消息

对比HTTPWebSocket
通信方向客户端→服务端双向通信
连接方式每次请求新建连接建立连接后保持
实时性需要轮询即时推送
适用场景普通 API聊天、通知、实时数据

二、SpringBoot 整合 WebSocket

1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 配置 WebSocket

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

三、核心:WebSocket 服务端

@Component
@ServerEndpoint("/ws/{userId}")
@Slf4j
public class WebSocketServer {

    /** 存放每个用户的 WebSocket 连接 */
    private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 连接建立成功
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        sessionMap.put(userId, session);
        log.info("用户 {} 已连接,当前在线人数: {}", userId, sessionMap.size());
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        sessionMap.remove(userId);
        log.info("用户 {} 已断开,当前在线人数: {}", userId, sessionMap.size());
    }

    /**
     * 收到客户端消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam("userId") String userId) {
        log.info("收到用户 {} 的消息: {}", userId, message);
    }

    /**
     * 发生错误
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket 错误", error);
    }

    /**
     * 向指定用户推送消息
     */
    public static void sendToUser(String userId, String message) {
        Session session = sessionMap.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("推送消息失败", e);
            }
        }
    }

    /**
     * 向所有用户广播消息
     */
    public static void broadcast(String message) {
        sessionMap.values().forEach(session -> {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("广播消息失败", e);
            }
        });
    }

    /**
     * 获取在线用户数
     */
    public static int getOnlineCount() {
        return sessionMap.size();
    }
}

四、服务端主动推送消息

在 Controller 中调用 WebSocket 推送消息:

@RestController
@RequestMapping("/push")
public class PushController {

    /**
     * 推送给指定用户
     */
    @PostMapping("/user/{userId}")
    public ResultVO pushToUser(@PathVariable String userId, @RequestBody String message) {
        WebSocketServer.sendToUser(userId, message);
        return ResultVO.success("推送成功");
    }

    /**
     * 广播给所有用户
     */
    @PostMapping("/broadcast")
    public ResultVO broadcast(@RequestBody String message) {
        WebSocketServer.broadcast(message);
        return ResultVO.success("广播成功");
    }

    /**
     * 获取在线人数
     */
    @GetMapping("/online")
    public ResultVO getOnlineCount() {
        return ResultVO.success(WebSocketServer.getOnlineCount());
    }
}

五、前端页面连接 WebSocket

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket 消息推送</title>
</head>
<body>
    <h3>WebSocket 实时消息</h3>
    <div>
        <input type="text" id="userId" placeholder="用户ID" value="user001">
        <button onclick="connect()">连接</button>
        <button onclick="disconnect()">断开</button>
    </div>
    <div style="margin-top: 10px;">
        <div id="messages" style="border:1px solid #ccc; height:300px; overflow-y:scroll; padding:10px;"></div>
    </div>
    <script>
        let websocket = null;
        function connect() {
            const userId = document.getElementById('userId').value;
            websocket = new WebSocket('ws://localhost:8080/ws/' + userId);
            websocket.onopen = function() {
                appendMessage('✅ 连接成功,用户ID: ' + userId);
            };
            websocket.onmessage = function(event) {
                appendMessage('📩 收到消息: ' + event.data);
            };
            websocket.onclose = function() {
                appendMessage('❌ 连接已断开');
            };
            websocket.onerror = function(error) {
                appendMessage('⚠️ 连接出错');
            };
        }
        function disconnect() {
            if (websocket) {
                websocket.close();
            }
        }
        function appendMessage(msg) {
            const div = document.getElementById('messages');
            div.innerHTML += '<div>' + msg + '</div>';
            div.scrollTop = div.scrollHeight;
        }
    </script>
</body>
</html>

六、实战:系统通知推送

实际项目中,在业务代码中调用推送:

@Service
public class OrderService {

    public void createOrder(Order order) {
        // 1. 保存订单
        orderMapper.insert(order);

        // 2. 推送通知给用户
        JSONObject msg = new JSONObject();
        msg.put("type", "order_notify");
        msg.put("orderId", order.getId());
        msg.put("message", "您的订单已创建成功");

        WebSocketServer.sendToUser(
            order.getUserId().toString(),
            msg.toJSONString()
        );
    }
}

客户端收到消息后,根据 type 字段做相应处理:

websocket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    if (data.type === 'order_notify') {
        showNotification('📦 ' + data.message);
    } else if (data.type === 'system_notify') {
        showNotification('🔔 ' + data.message);
    }
};

七、常见问题

1. WebSocket 连接被拦截

如果项目中有拦截器,需要放行 WebSocket 请求:

registry.addInterceptor(loginInterceptor)
    .addPathPatterns("/**")
    .excludePathPatterns("/ws/**");  // 放行 WebSocket

2. 跨域问题

@Bean
public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
}

// WebSocket 原生不支持跨域配置
// SpringBoot 方式需要在配置中添加
@Bean
public WebSocketConfigurer webSocketConfigurer() {
    return registry -> registry.addHandler(webSocketHandler(), "/ws/{userId}")
            .setAllowedOrigins("*");
}

3. 心跳保活

WebSocket 长时间空闲可能被断开,定期发送心跳包:

// 客户端每 30 秒发送心跳
setInterval(() => {
    if (websocket && websocket.readyState === WebSocket.OPEN) {
        websocket.send('ping');
    }
}, 30000);

总结

WebSocket 是实时消息推送的首选方案。SpringBoot 整合 WebSocket 非常简单,三步走:

  1. 引入依赖 + 配置
  2. 编写 WebSocketServer —— @OnOpen、@OnClose、@OnMessage
  3. 业务代码调用推送 —— 面向指定用户或广播

到此这篇关于SpringBoot+WebSocket实现实时消息推送系统(在线聊天/通知)的文章就介绍到这了,更多相关SpringBoot WebSocket实时消息推送内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文