当前位置: 首页 > news >正文

webSocket发送实时通知实例

在 Spring Boot 中实现前端调用接口执行任务并通过 WebSocket 实时推送任务状态,可以按照以下步骤操作:


1. 添加依赖

pom.xml 中添加 WebSocket 和 JSON 支持依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

2. 配置 WebSocket

创建一个 WebSocket 配置类,启用 STOMP 协议和消息代理:

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 前端连接端点
        registry.addEndpoint("/ws")
                .setAllowedOrigins("*") // 允许跨域
                .withSockJS();         // 支持 SockJS
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 启用内存消息代理,前缀为 /topic 的消息会发送到代理
        registry.enableSimpleBroker("/topic");
        // 客户端发送消息的前缀(可选,若需双向通信)
        registry.setApplicationDestinationPrefixes("/app");
    }
}

3. 定义任务状态消息对象

创建一个 DTO 类表示任务状态消息:

public class TaskStatusMessage {
    private String taskId;
    private String status;  // "STARTED", "SUCCESS", "FAILED"
    private String message;

    // 构造方法、Getter 和 Setter
}

4. 编写任务服务与 WebSocket 推送

在服务层中执行任务并通过 SimpMessagingTemplate 推送消息:

import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class TaskService {

    private final SimpMessagingTemplate messagingTemplate;

    public TaskService(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @Async // 异步执行任务
    public void executeTask(String taskId) {
        try {
            // 发送任务开始通知
            sendStatus(taskId, "STARTED", "任务开始执行");

            // 模拟耗时操作
            Thread.sleep(5000);

            // 模拟成功或失败
            boolean success = Math.random() > 0.5;
            if (success) {
                sendStatus(taskId, "SUCCESS", "任务执行成功");
            } else {
                sendStatus(taskId, "FAILED", "任务执行失败");
            }
        } catch (InterruptedException e) {
            sendStatus(taskId, "FAILED", "任务被中断");
        }
    }

    private void sendStatus(String taskId, String status, String message) {
        TaskStatusMessage msg = new TaskStatusMessage();
        msg.setTaskId(taskId);
        msg.setStatus(status);
        msg.setMessage(message);
        // 推送消息到 /topic/task-status
        messagingTemplate.convertAndSend("/topic/task-status", msg);
    }
}

5. 创建任务触发接口

编写 REST 接口触发任务并返回任务 ID:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;

@RestController
public class TaskController {

    private final TaskService taskService;

    public TaskController(TaskService taskService) {
        this.taskService = taskService;
    }

    @GetMapping("/start-task")
    public String startTask() {
        String taskId = UUID.randomUUID().toString();
        taskService.executeTask(taskId);
        return taskId; // 返回任务ID供前端订阅状态
    }
}

6. 前端实现(JavaScript)

使用 SockJS 和 Stomp.js 连接 WebSocket 并订阅状态:

<!DOCTYPE html>
<html>
<head>
    <title>任务状态监控</title>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.0/dist/sockjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@6.1.2/dist/stomp.umd.min.js"></script>
</head>
<body>
    <button onclick="startTask()">启动任务</button>
    <div id="status"></div>

    <script>
        let stompClient = null;
        const taskIdElement = document.createElement('div');
        document.body.appendChild(taskIdElement);

        // 连接 WebSocket
        function connect() {
            const socket = new SockJS('http://localhost:8080/ws');
            stompClient = Stomp.over(socket);
            stompClient.connect({}, () => {
                console.log('WebSocket 已连接');
            });
        }

        // 启动任务
        function startTask() {
            fetch('/start-task')
                .then(response => response.text())
                .then(taskId => {
                    taskIdElement.textContent = `任务ID: ${taskId}`;
                    subscribeToTaskStatus(taskId);
                });
        }

        // 订阅任务状态
        function subscribeToTaskStatus(taskId) {
            if (stompClient) {
                stompClient.subscribe(`/topic/task-status`, (message) => {
                    const statusMsg = JSON.parse(message.body);
                    if (statusMsg.taskId === taskId) {
                        document.getElementById('status').innerHTML += 
                            `<p>状态: ${statusMsg.status} - ${statusMsg.message}</p>`;
                    }
                });
            }
        }

        // 初始化连接
        connect();
    </script>
</body>
</html>

7. 启用异步支持

在 Spring Boot 主类添加 @EnableAsync

import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

8. 运行与测试

  1. 启动 Spring Boot 应用。
  2. 访问前端页面,点击按钮启动任务。
  3. 观察实时状态更新。

关键点说明

  1. WebSocket 连接:前端通过 /ws 端点连接,使用 SockJS 和 STOMP。
  2. 消息推送:服务端通过 SimpMessagingTemplate/topic/task-status 发送消息。
  3. 异步任务:使用 @Async 避免阻塞主线程,需配置线程池(默认使用 SimpleAsyncTaskExecutor)。
  4. 消息过滤:前端根据 taskId 过滤属于自己的任务状态。

扩展优化

  • 线程池配置:自定义 @Async 的线程池,避免无限制创建线程。
    @Configuration
    @EnableAsync
    public class AsyncConfig implements AsyncConfigurer {
        @Override
        public Executor getAsyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(4);
            executor.setMaxPoolSize(10);
            executor.setQueueCapacity(50);
            executor.initialize();
            return executor;
        }
    }
    
  • 错误处理:在 @Async 方法中添加异常处理逻辑。
  • 消息持久化:如需持久化任务状态,可集成数据库记录历史。

相关文章:

  • Spring Cloud — Hystrix 服务隔离、请求缓存及合并
  • 科普mfc100.dll丢失怎么办?有没有简单的方法修复mfc100.dll文件
  • QILSTE H4-116BRG/5M 全彩LED灯珠 发光二极管LED
  • 【多模态大模型】端侧语音大模型minicpm-o:手机上的 GPT-4o 级多模态大模型
  • 《Keras 3 : 使用迁移学习进行关键点检测》
  • 常用的 JVM 参数:配置与优化指南
  • MySQL主从架构
  • 【git】合并多个提交记录
  • C++学习笔记第一天(vs工程创建+基本知识)
  • Tesla T4 显卡 Linux 64-bit Ubuntu 24.04 驱动和cuda系统支持版本
  • 应用层的协议-http/https的状态码
  • Mac下常用命令
  • 【产品小白】社交类app怎么设计
  • 数据结构-图-找出星型图的中心节点
  • 服务器socket端口绑定失败解决方案
  • 数学建模之数学模型-1:线性规划
  • 【天线】IFA天线知识点摘抄
  • 1.20作业
  • github 怎么创建一个私有repository 并从另外一台电脑拉取下来更新
  • cuda安装
  • 开封+网站建设+网络推广/南宁seo优化
  • 衡水注册公司流程和费用/独立站seo
  • 开发小程序定制公司/衡阳有实力seo优化
  • 个人衣服定制店铺/seo做的比较好的公司
  • 电商网站统计怎么做/经典广告语
  • 网站查不到备案/惠州seo外包平台