WebSocket

WebSocket

实现一个简单的webSocket,实现异常信息的推送,重试等

1.引入依赖

pom 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.创建实体类

java 复制代码
@Data
public class ExceptionMessage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String message;
    private LocalDateTime createdAt;
    // 'PENDING', 'SENT', 'FAILED'
    private String status;
    private int retries;
    private LocalDateTime retryAt;
}

3.创建接口

java 复制代码
public interface ExceptionMessageMapper extends JpaRepository<ExceptionMessage, Long> {

    // 获取所有状态为PENDING的消息
    List<ExceptionMessage> findByStatus(String status);

    // 获取重试时间早于当前时间的消息
    List<ExceptionMessage> findByStatusAndRetryAtBefore(String status, LocalDateTime now);
}

4.异常消息服务

java 复制代码
@Service
public class ExceptionService {

    private static final int MAX_RETRIES = 3;
    private static final long RETRY_INTERVAL_MS = 5000;  // 5秒重试间隔

    private final ExceptionMessageMapper messageMapper;
    private final SimpMessagingTemplate messagingTemplate;

    @Autowired
    public ExceptionService(ExceptionMessageMapper messageMapper, SimpMessagingTemplate messagingTemplate) {
        this.messageMapper = messageMapper;
        this.messagingTemplate = messagingTemplate;
    }

    // 存储异常消息到数据库
    @Transactional
    public void storeExceptionMessage(String exceptionData) {
        ExceptionMessage message = new ExceptionMessage();
        message.setMessage(exceptionData);
        message.setStatus(StatusEnum.PENDING.getStringVal());  // 初始状态为PENDING
        message.setRetries(0);
        message.setCreatedAt(LocalDateTime.now());
        messageMapper.save(message);
    }

    // 向前端发送消息
    public void sendMessage(String exceptionData) {
        messagingTemplate.convertAndSend(TopicEnum.EXCEPTIONS.getStringVal(), exceptionData);
    }

    // 发送单个消息并更新状态
    @Transactional
    public void processMessage(ExceptionMessage message) {
        try {
            // 存储消息到数据库
            storeExceptionMessage(message.getMessage());
            // 推送消息到前端
            sendMessage(message.getMessage());

            // 更新消息状态为SENT
            message.setStatus(StatusEnum.SENT.getStringVal());
            messageMapper.save(message);

        } catch (Exception e) {
            // 如果发送失败,更新为FAILED并设置重试时间
            message.setStatus(StatusEnum.FAILED.getStringVal());
            message.setRetryAt(LocalDateTime.now().plus(Duration.ofMillis(RETRY_INTERVAL_MS)));
            message.setRetries(message.getRetries() + 1);
            messageMapper.save(message);
        }
    }

    // 重试未成功发送的消息
    public void retryFailedMessages() {
        List<ExceptionMessage> failedMessages = messageMapper.findByStatusAndRetryAtBefore(StatusEnum.FAILED.getStringVal(), LocalDateTime.now());

        for (ExceptionMessage message : failedMessages) {
            if (message.getRetries() < MAX_RETRIES) {
                processMessage(message);
            } else {
                // 超过最大重试次数,放弃推送
                message.setStatus(StatusEnum.GIVE_UP.getStringVal());
                messageMapper.save(message);
            }
        }
    }

    // 重新发送所有PENDING状态的消息
    public void resendPendingMessages() {
        List<ExceptionMessage> pendingMessages = messageMapper.findByStatus(StatusEnum.PENDING.getStringVal());

        for (ExceptionMessage message : pendingMessages) {
            processMessage(message);
        }
    }
}

5.WebSocket 控制器

处理WebSocket连接和消息的推送。

java 复制代码
@RestController
@RequestMapping(value = "/app")
public class WebSocketController {
    private final org.example.webSocket.service.ExceptionService exceptionService;

    public WebSocketController(org.example.webSocket.service.ExceptionService exceptionService) {
        this.exceptionService = exceptionService;
    }

    /**
     * 由前端通过 STOMP 协议 调用的,这是一个处理连接请求的方法。
     * 每当前端连接到 WebSocket 时,会触发此方法执行,从而执行恢复未发送的消息
     **/
    @MessageMapping("/connect")
    public void handleConnect() {
        exceptionService.resendPendingMessages();
    }

    /**
     * 每5秒定时重试失败的消息
     **/
    @Scheduled(fixedRate = 5000)
    public void retryFailedMessages() {
        exceptionService.retryFailedMessages();
    }
}

6.配置 WebSocket

在Spring Boot中启用WebSocket。

java 复制代码
/**
 * webSocket配置类,注册WebSocket端点
 */
@Configuration
@EnableWebSocketMessageBroker
@EnableScheduling
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 启用简单的消息代理,用于处理客户端的订阅(如:/topic/xxx)
        registry.enableSimpleBroker("/topic");
        // 设置应用程序的前缀,用于处理来自客户端的消息(如:/app/xxx)
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册 WebSocket 端点,客户端通过此端点连接 WebSocket 服务
        registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
    }
}

7.客户端 (Vue.js)

在Vue前端,你需要在WebSocket连接成功时向后端发送连接请求以获取未发送的消息。

vue 复制代码
import { Stomp } from '@stomp/stompjs';
import SockJS from 'sockjs-client';

export default {
  data() {
    return {
      exceptionData: [],
      exceptionCount: 0,
    };
  },
  mounted() {
    this.connectToWebSocket();
  },
  methods: {
    connectToWebSocket() {
      const socket = new SockJS('/ws');
      const stompClient = Stomp.over(socket);

      stompClient.connect({}, (frame) => {
        // 订阅异常消息
        stompClient.subscribe('/topic/exceptions', (message) => {
          this.exceptionData.push(message.body);
          this.exceptionCount = this.exceptionData.length;
        });

        // 发送连接请求,获取未发送的消息
        stompClient.send("/app/connect");
      });
    }
  }
};
相关推荐
龙少95434 分钟前
【springboot中最适合用什么技术来实现在线聊天】
java·spring boot·后端
陶然同学5 分钟前
【小程序】wxss与rpx单位以及全局样式和局部样式
java·微信小程序·小程序
@Java小牛马6 分钟前
排序算法原理及其实现
java·数据结构·算法·排序算法
vvw&14 分钟前
如何在 Ubuntu 22.04 上安装并开始使用 RabbitMQ
java·linux·运维·服务器·spring·ubuntu·rabbitmq
万琛1 小时前
【Java-tesseract】OCR图片文本识别
java·ocr
励志成为大佬的小杨1 小时前
c语言中的枚举类型
java·c语言·前端
yava_free1 小时前
指定Bean加载顺序的能力
java·开发语言
whisperrr.1 小时前
探索JDBC:Java数据库连接的艺术与魅力
java·开发语言·数据库
boy快快长大1 小时前
【NebulaGraph】查询案例(六)
java·服务器·数据库
重生之Java开发工程师1 小时前
JVM 主要组成部分与内存区域
java·jvm·面试