Websocket定时监控+开关+参数传递

文章介绍了如何利用WebSocket进行设备温度监控,通过配置WebSocket实现服务器与客户端的长连接,避免频繁HTTP请求造成的资源浪费。同时,文中提到了一个定时任务控制层,用于开启和关闭温度监控任务,任务开关使用了ScheduledFuture和ThreadPoolTaskScheduler。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

举个例子,当开发中遇到了这样的需求时

需求:需要定时监控多个设备的温度情况,并且需要设置开关控制监控任务

       因为需要定时不断的给前端返回我们业务处理好的数据,这个时候HTTP请求显然不能很好的满足我们的需求,除非用定时器不断的发请求,这样会造成资源的浪费,而websocket是一种长连接,一旦连接服务端就就可以不断地向客户端推送数据,不用一直三次握手四次握手。

       因此这里采用websocket的方式来实现业务


1. 配置WebSocket首先需要建立WebSocket的配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * websocket 配置
 * 
 * @author 
 */
@Configuration
public class WebSocketConfig
{
    @Bean
    public ServerEndpointExporter serverEndpointExporter()
    {
        return new ServerEndpointExporter();
    }
}

 2.具体配置WebSocket

import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.websocket.SemaphoreUtils;
import com.ruoyi.framework.websocket.WebSocketUsers;
import com.ruoyi.system.domain.Equipment;
import com.ruoyi.system.service.IEquipmentService;
import constant.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

/**
 * websocket 消息处理
 *
 * @author 
 */

//交给Spring来管理
@Component
//建立连接的端点
@ServerEndpoint("/websocket/{id}")
public class WebSocketServer {
    /**
     * WebSocketServer 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 默认最多允许同时在线人数100
     */
    public static int socketMaxOnlineCount = 100;

    /**
     * 信号量控制在线人数
     */
    private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);


    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */
    public static ConcurrentHashMap<Integer, WebSocketServer> webSocketSet = new ConcurrentHashMap<Integer, WebSocketServer>();

    /**
     * concurrent包的线程安全hashMap,用来存放每一个session里的参数信息
     */
    private static ConcurrentHashMap<String, String> hashMap = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 传过来的id 1:搜索设备,2:温度报警,3:查询设备是否断开连接
     */
    private Integer id = 0;


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam(value = "id") Integer param, Session session) throws Exception {
        boolean semaphoreFlag = false;
        // 尝试获取信号量
        semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);
        if (!semaphoreFlag) {
            // 未获取到信号量
            LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount);
            //WebSocketUsers.sendMessageToUserByText(session, "忙碌中,请稍后再试...");
            session.close();
        } else {
            // 添加用户
            //WebSocketUsers.put(session.getId(), session);
            //接收客户端编号并且加入到set集合当中
            id = param;
            this.session = session;
            webSocketSet.put(param, this);

            //获取session当中的参数
            Map<String, List<String>> map = session.getRequestParameterMap();
            if (!map.isEmpty()){
                //把取出来的参数存入hashmap当中
                //当连接为ws://127.0.0.1:8080/websocket/2?equipmentIds=1时
                hashMap.put(session.getId(),map.get("equipmentIds").get(0));
            }
            LOGGER.info("\n 建立连接 - {}", session);
            //LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size());
        }
    }

    /**
     * 连接关闭时处理
     */
    @OnClose
    public void onClose(Session session) {
        LOGGER.info("\n 关闭连接 - {}", session);
        // 移除用户
        WebSocketUsers.remove(session.getId());
        webSocketSet.remove(id);
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void onError(Session session, Throwable exception) throws Exception {
        if (session.isOpen()) {
            // 关闭连接
            session.close();
        }
        String sessionId = session.getId();
        LOGGER.info("\n 连接异常 - {}", sessionId);
        LOGGER.info("\n 异常信息 - {}", exception);
        // 移出用户
        //WebSocketUsers.remove(sessionId);
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    public void sendMessage(String message) throws IOException {
        synchronized (session) {
            getSession().getBasicRemote().sendText(message);
        }
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
       //业务
    }


    /**
     * 给指定的人发送消息
     *
     * @param message
     */
    public static void sendToMessageById(Integer id, String message) {
        try {
            if (webSocketSet.get(id) != null) {
                webSocketSet.get(id).sendMessage(message);
            } else {
                System.out.println("webSocketSet中没有此key,不推送消息");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Session getSession() {
        return session;
    }

    public static ConcurrentHashMap<String, String> getHashMap() {
        return hashMap;
    }
}

3.信号量工具类(控制在线连接数)

import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 信号量相关处理
 * 
 * @author ruoyi
 */
public class SemaphoreUtils
{
    /**
     * SemaphoreUtils 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class);

    /**
     * 获取信号量
     * 
     * @param semaphore
     * @return
     */
    public static boolean tryAcquire(Semaphore semaphore)
    {
        boolean flag = false;

        try
        {
            flag = semaphore.tryAcquire();
        }
        catch (Exception e)
        {
            LOGGER.error("获取信号量异常", e);
        }

        return flag;
    }

    /**
     * 释放信号量
     * 
     * @param semaphore
     */
    public static void release(Semaphore semaphore)
    {

        try
        {
            semaphore.release();
        }
        catch (Exception e)
        {
            LOGGER.error("释放信号量异常", e);
        }
    }
}

4.控制层监控任务开关

    private ScheduledFuture<?> futureAlarmTempMonitor;

    /**
     * 用来存储温度配置信息
     */
    public static ConcurrentHashMap<String, String> hashMap = new ConcurrentHashMap<>();

    /**
     * 用来存储每一台设备的报警定时任务
     */
    public static ConcurrentHashMap<Long,ScheduledFuture<?>> equipmentTask=new ConcurrentHashMap<>();

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    /**
     * 开启温度监控定时任务(整帧检测)
     *
     */
    @ApiOperation("开启温度监控定时任务")
    @Log(title = "开启温度监控定时任务", businessType = BusinessType.INSERT)
    @PostMapping("/startAlarmTempMonitor")
    public AjaxResult startAlarmTempMonitor(@RequestBody TempAlarmConfig tempAlarmConfig) {
        //将温度配置的参数存入hashMap当中
        hashMap.put(Constants.Temp_Alarm_Config +tempAlarmConfig.getEquipmentId(), JSON.toJSONString(tempAlarmConfig));
        //方法会创建一个定时计划ScheduledFuture,在这个方法需要添加两个参数,Runnable(线程接口类) 和CronTrigger(定时任务触发器)
        /* 该种方法可以使用cron表达式 最小单位为秒
        futureAlarmTempMonitor = threadPoolTaskScheduler.schedule(new TempAlarmRunnable(), new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //周期为帧频执行一次
                return new CronTrigger(CronExpressionUtils.conversion(imageFramerate)).nextExecutionTime(triggerContext);
            }
        });*/
        //定时计划,最小单位为毫秒,在这里每五秒执行一次
        futureAlarmTempMonitor = threadPoolTaskScheduler.scheduleWithFixedDelay(new TempAlarmRunnable(), 5000);
        //把定时任务保存进hashMap当中
        equipmentTask.put(tempAlarmConfig.getEquipmentId(), futureAlarmTempMonitor);
        System.out.println("开启温度监控定时任务");
        //报警配置存到数据库当中
        //业务

        return AjaxResult.success();
    }

    @ApiOperation("关闭温度监控定时任务")
    @PostMapping("/stopAlarmTempMonitor/{equipmentId}")
    //这个id在TempAlarmConfig实体类当中 
    public AjaxResult stopAlarmTempMonitor(@PathVariable Long equipmentId){
        if (!equipmentTask.isEmpty()){
            ScheduledFuture<?> scheduledFuture = equipmentTask.get(equipmentId);
            scheduledFuture.cancel(true);
        }
        System.out.println("关闭温度监控定时任务");
        return AjaxResult.success();
    }

5.实现Runnable接口

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.AlarmData;
import com.ruoyi.system.domain.Equipment;
import com.ruoyi.system.domain.TempAlarmConfig;
import com.ruoyi.system.domain.TempData;
import com.ruoyi.system.sdk.SdkManager;
import com.ruoyi.system.service.IAlarmDataService;
import com.ruoyi.system.service.IEquipmentService;
import com.ruoyi.system.service.ITempAlarmConfigService;
import com.ruoyi.web.controller.thermometry.AlarmDataController;
import com.ruoyi.web.controller.websocket.WebSocketServer;
import com.ruoyi.web.core.config.EquipmentContext;
import org.springframework.beans.factory.annotation.Autowired;

import javax.swing.*;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 监控温度报警线程
 * sendToMessageById:2  表示监控温度报警的webSocket
 */
public class TempAlarmRunnable implements Runnable {

    @Override
    public void run() {
        if (CollectionUtil.isNotEmpty(WebSocketServer.webSocketSet)) {
            //取出存入hashMap当中的值
            ConcurrentHashMap<String, String> hashMap = AlarmDataController.hashMap;
            //取出对应的设备ID 2表示对应的温度报警webSocket
            String sessionId = WebSocketServer.webSocketSet.get(2).getSession().getId();
            String equipmentId = WebSocketServer.getHashMap().get(sessionId);
            if (!hashMap.isEmpty()) {
                String s = hashMap.get(Constants.Temp_Alarm_Config + equipmentId);
                //获取温度报警配置参数
                TempAlarmConfig tempAlarmConfig = JSON.parseObject(s, TempAlarmConfig.class);
                selectAlarmData(tempAlarmConfig);
            }
        }
    }

    public void selectAlarmData(TempAlarmConfig tempAlarmConfig) {
 
       //给前台返回消息
       WebSocketServer.sendToMessageById(2,JSON.toJSONString(业务));
    }
}

that's all

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值