场景
- 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;
@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;
}
@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);
}
@MessageMapping("/heartBeat")
public void heartBeatMapping() {
log.info(">>>>>>用户发来心跳,时间:{}", LocalDateTime.now());
isActive = System.currentTimeMillis()/1000;
}
@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());
}
@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;
@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;
}
@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);
}
}
}