Spring stomp 服务端处理取消订阅事件

场景

  • spring 集成 stomp,发送消息,进行前后端websocket通信
  • 前端引入相关js,进行订阅和取消订阅
  • 实际业务是,程序作为硬件产品的上位机,按照约定的通信协议,与上位机使用socket通信。订阅和取消订阅,都需要发送请求给硬件产品,才会实际进行订阅/取消订阅。
  • 因此,前端发起订阅和取消订阅时,服务端还需要进行一些业务处理,故需要监听订阅/取消订阅事件,进行自己的业务

编码

  • 建立一个 stomp 控制类,接收订阅请求,监听事件
  • 使用 @SubscribeMapping接收订阅请求
  • 使用@MessageMapping接收前端send请求,此处用于接收前端定时主动发起的心跳请求
  • 使用@EventListener注解监听stomp事件,此处我暂时只监听订阅/取消订阅两个事件
  • 使用handleSessionSubscribeEvent(SessionSubscribeEvent event)监听订阅
  • 使用handleSessionUnsubscribeEvent(SessionUnsubscribeEvent event)监听取消订阅
package com.cy.controller;

import com.alibaba.fastjson.JSON;
import com.cy.config.DataConfig;
import com.cy.service.UpperComputerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;
import org.springframework.web.socket.messaging.SessionUnsubscribeEvent;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * stomp 控制类
 * @author yanyulin
 * @date 2023-1-11 13:45:25
 **/
@RestController
public class StompSocketHandler {
    
    

    /**
     * 与前端保持的最后一次连接的时间
     */
    public static Long isActive = 0L;

    /**
     * 已经订阅的连接
     */
    public static Map<String, String> subscribeTopics = new HashMap<>();

    private static final Logger log = LoggerFactory.getLogger(StompSocketHandler.class);

    private final UpperComputerService upperComputerService;

    public StompSocketHandler(UpperComputerService upperComputerService) {
    
    
        this.upperComputerService = upperComputerService;
    }

    /**
     * 订阅
     * @param topic
     */
    @SubscribeMapping("{topic}")
    public void subscribeMapping(@DestinationVariable("topic") final String topic) {
    
    
        int equipmentNo = 0;
        if (DataConfig.thresholdList.size() > 0) {
    
    
            equipmentNo = DataConfig.thresholdList.get(0).getEquipmentNo();
        }
        int action = 1;
        log.info(">>>>>>用户已订阅,equipmentNo={}, topic={}, action={}", equipmentNo, topic, action);
        upperComputerService.subscribe(equipmentNo, topic, action);
    }

    /**
     * 心跳机制保活,记录心跳发送来的秒数(如果1分钟没有心跳,取消所有订阅)
     */
    @MessageMapping("/heartBeat")
    public void heartBeatMapping() {
    
    
        log.info(">>>>>>用户发来心跳,时间:{}", LocalDateTime.now());
        isActive = System.currentTimeMillis()/1000;
    }

    /**
     * 监听订阅事件,将id写入内存
     * @param event
     */
    @EventListener
    private void handleSessionSubscribeEvent(SessionSubscribeEvent event) {
    
    
        log.info("Subscribe event : {}", JSON.toJSONString(event));
        MessageHeaders headers = event.getMessage().getHeaders();
        subscribeTopics.put(event.getMessage().getHeaders().get("simpSubscriptionId").toString(), headers.get("simpDestination").toString());
    }

    /**
     * 监听取消订阅事件,前端取消订阅时,向盒子发请求取消订阅
     * @param event
     */
    @EventListener
    private void handleSessionUnsubscribeEvent(SessionUnsubscribeEvent event) {
    
    
        log.info("Unsubscribe event : {}", JSON.toJSONString(event));
        int equipmentNo = 0;
        if (DataConfig.thresholdList.size() > 0) {
    
    
            equipmentNo = DataConfig.thresholdList.get(0).getEquipmentNo();
        }
        int action = 0;
        String topic = subscribeTopics.get(event.getMessage().getHeaders().get("simpSubscriptionId").toString());
        log.info(">>>>>>用户已取消订阅,equipmentNo={}, topic={}, action={}", equipmentNo, topic, action);
        upperComputerService.subscribe(equipmentNo, topic, action);
    }
}
  • 我们的业务场景是一个或少量用户使用,所以当浏览器关闭后,心跳请求会结束一段时间后,服务端要能取消订阅
  • 增加一个定时任务,监听心跳请求,当1分钟没接收到心跳请求时,需要把所有已订阅的取消掉
package com.cy.config;


import com.cy.controller.StompSocketHandler;
import com.cy.service.UpperComputerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.HashSet;
import java.util.Set;

/**
 * 订阅保持定时任务
 * @author yanyulin
 * @date 2023-1-11 16:53:00
 */
@Component
@EnableScheduling
public class SubscribeHoldTask {
    
    

    private static final Logger log = LoggerFactory.getLogger(SubscribeHoldTask.class);

    private final UpperComputerService upperComputerService;

    public SubscribeHoldTask(UpperComputerService upperComputerService) {
    
    
        this.upperComputerService = upperComputerService;
    }

    /**
     * 1分钟执行一次,如果1分钟没有心跳,取消所有订阅
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void calculate() {
    
    
        log.info("----{}----订阅保持定时任务, 1分钟执行一次,如果1分钟没有心跳,取消所有订阅", Instant.now());
        try {
    
    
            long now = System.currentTimeMillis()/1000;
            long isActive = StompSocketHandler.isActive;
            if (now - isActive > 60) {
    
    
                Set<String> topics = new HashSet<>();
                StompSocketHandler.subscribeTopics.forEach((k, v) -> topics.add(v));
                if (topics.size() > 0) {
    
    
                    int equipmentNo = 0;
                    if (DataConfig.thresholdList.size() > 0) {
    
    
                        equipmentNo = DataConfig.thresholdList.get(0).getEquipmentNo();
                    }
                    int action = 0;
                    for (String topic : topics) {
    
    
                        upperComputerService.subscribe(equipmentNo, topic, action);
                        log.info(">>>>>>断开连接,取消订阅,equipmentNo={}, topic={}", equipmentNo, topic);
                    }
                }
                StompSocketHandler.subscribeTopics.clear();
            }
        } catch (Exception e) {
    
    
            log.error("SubscribeHoldTask::calculate()  error..", e);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/u010882234/article/details/128722921
今日推荐