springboot webscoket示例:增加定时心跳逻辑

websocket服务端增加定时发送心跳机制

@ServerEndpoint(value = "/websocket/{uuid}")
@Component
public class DevMessageHandleController {private static final Logger logger = LoggerFactory.getLogger(DevMessageHandleController.class);//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketController对象。public static CopyOnWriteArraySet<DevMessageHandleController> webSocketSet = new CopyOnWriteArraySet<>();// 使用ConcurrentHashMap来存储用户ID和WebSocket会话对象的映射。private static ConcurrentHashMap<String, DevMessageHandleController> webSocketMap = new ConcurrentHashMap<>();//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;private String uuid;// 心跳尝试次数private AtomicInteger heartbeatAttempts;@OnOpenpublic void onOpen(@PathParam("uuid") String uuid, Session session) {logger.info("uuid: {}, sessionId: {}", uuid, session.getId());// 将新建立的会话添加到webSocketMap中try {if (webSocketMap.containsKey(uuid)) {//如果有旧连接就先断开webSocketMap.get(uuid).session.close();webSocketSet.remove(webSocketMap.get(uuid));}this.session = session;this.uuid = uuid;heartbeatAttempts = new AtomicInteger(0);webSocketSet.add(this); //加入set中webSocketMap.put(uuid, this); //加入map中// 其他代码...} catch (Exception e) {logger.error("onOpen error:" + e.getMessage());}}@OnClosepublic void onClose(@PathParam("uuid") String uuid, Session session) {logger.info("会话关闭");// 当会话关闭时,从webSocketSet中移除该会话webSocketSet.remove(this);// 当会话关闭时,从webSocketMap中移除该会话webSocketMap.remove(uuid);}// 收到客户端消息后调用的方法@OnMessagepublic void onMessage(String message, Session session) {logger.info("Message from client: " + message);if ("pong".equals(message)) {// 重置为0this.heartbeatAttempts.set(0);System.out.println("Received pong from: " + session.getId());}}// 发生错误时调用@OnErrorpublic void onError(Session session, Throwable error) {logger.error("发生错误 session:" + session.getId() + ",error:" + error);try {session.close();// 当会话关闭时,从webSocketSet中移除该会话webSocketSet.remove(this);// 当会话关闭时,从webSocketMap中移除该会话webSocketMap.remove(this.uuid);} catch (IOException e) {logger.error("onError error:" + e.getMessage());}}public void sendMessage(Session session, String msg) {logger.info("发送消息");try {//判断当前人员是否连接websocketif (session.isOpen()) {session.getAsyncRemote().sendText(msg);} else {session.close();webSocketSet.remove(this); //从set中删除webSocketMap.remove(this.uuid); //从map中删除}} catch (IOException e) {e.printStackTrace();}}public static CopyOnWriteArraySet<DevMessageHandleController> getWebSocketSet() {return webSocketSet;}public static void setWebSocketSet(CopyOnWriteArraySet<DevMessageHandleController> webSocketSet) {DevMessageHandleController.webSocketSet = webSocketSet;}public static ConcurrentHashMap<String, DevMessageHandleController> getWebSocketMap() {return webSocketMap;}public static void setWebSocketMap(ConcurrentHashMap<String, DevMessageHandleController> webSocketMap) {DevMessageHandleController.webSocketMap = webSocketMap;}public Session getSession() {return session;}public void setSession(Session session) {this.session = session;}public String getUuid() {return uuid;}public void setUuid(String uuid) {this.uuid = uuid;}public AtomicInteger getHeartbeatAttempts() {return heartbeatAttempts;}public void setHeartbeatAttempts(AtomicInteger heartbeatAttempts) {this.heartbeatAttempts = heartbeatAttempts;}
}

每间隔10s向客户端发送一次心跳

    private static final int MAX_HEARTBEAT_ATTEMPTS = 3;@Scheduled(fixedDelay = 10000)public void sendHeartBeat() {CopyOnWriteArraySet<DevMessageHandleController> webSocketSet;try {webSocketSet = DevMessageHandleController.getWebSocketSet();logger.info("连接数量:" + webSocketSet.size());if(webSocketSet.size() == 0){return;}logger.info("定时发送心跳");// 遍历所有连接,发送心跳webSocketSet.forEach(obj -> {Session session = obj.getSession();logger.info("sessionId:" + session.getId() +" 心跳ping发送次数:" + obj.getHeartbeatAttempts().get());if(obj.getHeartbeatAttempts().get() >= MAX_HEARTBEAT_ATTEMPTS) {try {session.close();} catch (IOException e) {e.printStackTrace();logger.error("session close error:" + e.getMessage());}} else {obj.getHeartbeatAttempts().incrementAndGet();if (session.isOpen()) {session.getAsyncRemote().sendText("ping");}}});}catch (Exception e){logger.error("发送心跳 error:" + e.getMessage());}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/7476.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数组扁平化

数组扁平化 输入&#xff1a;[1,[2,[3,5],5],6] 输出&#xff1a;[1,2,3,4,5,6] 方法一&#xff1a;递归 const transformArray (arr, res) > {for (let i 0; i < arr.length; i) {if (arr[i] instanceof Array) {transformArray(arr[i], res)} else {res.push(arr…

面试官:谈谈你知道的设计模式❓

创建型模式&#x1f527; 1&#xff09;单例模式&#xff08;Singleton&#xff09;&#x1f6a9; 确保一个类只有一个实例&#xff0c;并提供一个全局访问点。 2&#xff09;工厂方法模式&#xff08;Factory Method&#xff09;&#x1f6a9; 定义一个创建对象的接口&#…

YashanDB与帆软信创商业智能软件完成兼容互认证

近日&#xff0c;深圳计算科学研究院崖山数据库系统YashanDB与帆软信创商业智能软件&#xff08;V6.0&#xff09;顺利完成兼容性互认证&#xff0c;经严格测试&#xff0c;双方产品能够相互兼容&#xff0c;稳定运行。 崖山数据库系统YashanDB是深圳计算科学研究院自主研发设计…

构建第一个ArkTS应用之@LocalStorage:页面级UI状态存储

LocalStorage是页面级的UI状态存储&#xff0c;通过Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage也可以在UIAbility实例内&#xff0c;在页面间共享状态。 本文仅介绍LocalStorage使用场景和相关的装饰器&#xff1a;LocalStorageProp和LocalS…

openGauss学习笔记-276 openGauss性能调优-实际调优案例05-改建分区表

文章目录 openGauss学习笔记-276 openGauss性能调优-实际调优案例05-改建分区表276.1 现象描述276.2 优化分析openGauss学习笔记-276 openGauss性能调优-实际调优案例05-改建分区表 276.1 现象描述 如下简单SQL语句查询, 性能瓶颈点在normal_date的Scan上。 QUERY PLAN ----…

[开发|安卓] Android Studio 开发环境配置

Android Studio下载 Android Studio下载地址 下载SDK依赖 1.点击左上角菜单 2.选择工具 3.打开SDK管理中心 4.下载项目目标Android版本的SDK 配置安卓虚拟机 1.打开右上角的设备管理 2.选择合适的手机规格 3.下载并选择项目目标Android系统 4.点击完成配置 …

国内如何下载TikTOK,手机刷机教程

最近很多玩家都来问怎么刷机&#xff1f;手机环境怎么搭建&#xff1f;这里给大家整理了苹果IOS刷机教程 1.iOS下载教程 &#xff1a; 步骤一&#xff1a;手机调试 苹果手机系统配置推荐&#xff1a;iPhone6S以上&#xff0c;16G。 注意&#xff1a;如果是选择购入二手手机…

指针进阶(三)

嘿嘿,uu们,今天呢我们来剖析指针进阶的剩下部分,好啦,废话不多讲,开干! 1:回调函数 概念:回调函数是指一个通过函数指针调用的函数,如果将函数的地址作为参数传递给另外一个函数,当这个指针被用来调用所指向的函数时,那么这个被调用的函数就是回调函数.回调函数不是由该函数的…

2024年中国AI大模型产业发展报告,洞见下一个智能时代!

人民网财经研究院、至顶科技联合发布的《开启智能新时代&#xff1a;2024年中国AI大模型产业发展报告》,全面梳理了我国AI大模型产业的发展背景、现状、应用案例、面临的挑战以及未来趋势。报告指出,AI大模型是全球科技竞争的新高地、未来产业的新赛道、经济发展的新引擎,在我国…

【微磁学3D绘图工具探索】Paraview

文章目录 概要Material Science版块介绍 概要 微磁学中的磁学结构同时包括二维和三维&#xff0c;想要绘制得好看&#xff0c;结果清晰&#xff0c;那么就需要一些自己写的绘图代码之外的额外渲染功能&#xff0c;尤其是对于三维结构的处理&#xff0c;一个好的渲染能够带来很…

如何高效封装App?小猪APP分发平台一站式解决方案

在移动应用开发领域&#xff0c;App封装&#xff08;App Packaging&#xff09;是一个至关重要的环节&#xff0c;它不仅关乎应用的安全性&#xff0c;还直接影响到最终用户体验和市场推广策略。本文旨在通过实战指南&#xff0c;揭示如何高效完成App封装&#xff0c;并介绍如何…

微信报名活动链接怎么做

在数字营销日新月异的今天&#xff0c;微信作为拥有数亿用户的社交平台&#xff0c;早已成为品牌宣传的重要阵地。而微信报名活动链接&#xff0c;更是品牌吸引用户参与、提升活动影响力的关键工具。今天&#xff0c;就让我们一起探讨如何制作一个引人入胜的微信报名活动链接&a…

k8s安装(使用kubeadmin安装)

安装方式 使用kubeadmin安装 安装步骤 参考 k8s安装&#xff08;使用kubeadmin安装&#xff09;-CSDN博客

信创 | 2023年中国信创产业深度研究报告(完整版)

信创产业研究报告 免责声明&#xff1a;本文资料来源于“第一新声”&#xff0c;版权归原作者所有。如涉及作品版权问题&#xff0c;请与我们联系&#xff0c;我们将在第一时间协商版权问题或删除内容&#xff01; 获取文中相关的PPT资料&#xff0c;请关注文末公众号“程序员…

【JVM】内存结构

内存结构 Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区&#xff0c;其中有一些会随着虚拟机启动而创建&#xff0c;随着虚拟机退出而销毁。另外一些则是与线程一一对应的&#xff0c;这些与线程一一对应的数据区域会随着线程开始和结束而创建和销毁。 线程私有…

Baidu Comate 智能编码助手:编程新伙伴,效率新飞跃

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 写在前面 一、Baidu Comate智能编码助手简介…

STL——deque

双端队列&#xff0c;可以对头端进行插入删除操作 记录一个常遇到的问题 deque subscript out of range 使用了还未定义的空间&#xff0c;大概率是没有初始化就使用了下标或者其他方式进行数据访问。 与vector区别 内部实现方式&#xff1a;deque采用了分段连续存储的方式&…

自己动手写编译器:First 集合,Follow 集合和 Select 集合

在上一节内容&#xff0c;我们手动设计了解析跳转表&#xff0c;表的行对应当前解析堆栈上的非终结符&#xff0c;列对应当前读取的终结符&#xff0c;于是对应的表格数字表示当前应该采取哪个推导表达式。本节我们看看如何自动化构建解析跳转表。首先我们引入一个概念叫 First…

Java代码基础算法练习-年龄问题-2024.05.07

数学家维纳智力早熟&#xff0c;11岁就上了大学。一次&#xff0c;他参加某个重要会议&#xff0c;年轻的脸孔引人注目。于是有人询问他的年龄&#xff0c;他回答说&#xff1a;“我年龄的立方是个4位数。我年龄的4次方是个6位数。这10 个数字正好包含了从0到9这10个数字&#…