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

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 方法中添加异常处理逻辑。
  • 消息持久化:如需持久化任务状态,可集成数据库记录历史。

http://www.kler.cn/a/555697.html

相关文章:

  • 数据结构:串( Bunch)及其实现
  • Hadoop-HA集群部署
  • go 模块管理
  • 蓝桥杯 Java B 组之岛屿数量、二叉树路径和(区分DFS与回溯)
  • 【Linux网络编程】IP协议格式,解包步骤
  • List的基本功能(1)
  • 以ChatGPT为例解析大模型背后的技术
  • 数学建模:解锁智能计算的密码!
  • JVM 深入理解与性能优化
  • Qt常用控件之标签QLabel
  • 跨中心模型自适应牙齿分割|文献速递-医学影像人工智能进展
  • QT实战-基于QWidget实现的异形tip窗口
  • 前端vue的一些常见项目启动命令
  • R语言NIMBLE、Stan和INLA贝叶斯平滑及条件空间模型死亡率数据分析:提升疾病风险估计准确性...
  • 鸿蒙5.0实战案例:基于measure实现的文本测量
  • VSCode集成deepseek使用介绍(Visual Studio Code)
  • 2022年下半年试题一:论基于构件的软件开发方法及其应用
  • AI工作流+专业知识库+系统API的全流程任务自动化
  • 网络安全-js安全知识点与XSS常用payloads
  • HTML项目一键打包工具:HTML2EXE 最新版