当前位置: 首页 > news >正文

springboot 接入websocket实现定时推送消息到客户端

在这里插入图片描述

目录

  • 说明
  • 代码实现

说明

如标题,举例需求场景:
前端与后端websocket连接上后,多用户登录,后端根据不同用户定时发消息给前端用于展示

代码实现

1、

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

2、

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Component
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}

3、

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;// 交给IOC容器
@Component
// 如果去掉/{userId} 那就是不分用户 给连接上的用户统一发送消息
@ServerEndpoint("/websocket/{userId}")
@Slf4j
public class WebSocketService {// 这里用ConcurrentHashMap 因为他是一个线程安全的Mapprivate static ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketService>> userwebSocketMap = new ConcurrentHashMap<>();private static ConcurrentHashMap<String, Integer> count = new ConcurrentHashMap<>();private String userId;/** 与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 连接建立成功调用的方法** @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") final String userId) {this.session = session;this.userId = userId;System.out.println("session:" + session);System.out.println("userId:" + userId);if (!exitUser(userId)) {initUserInfo(userId);} else {CopyOnWriteArraySet<WebSocketService> webSocketServiceSet = getUserSocketSet(userId);webSocketServiceSet.add(this);userCountIncrease(userId);}System.out.println("有" + userId + "新连接加入!当前在线人数为" + getCurrUserCount(userId));}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {CopyOnWriteArraySet<WebSocketService> webSocketServiceSet = userwebSocketMap.get(userId);//从set中删除webSocketServiceSet.remove(this);//在线数减1userCountDecrement(userId);System.out.println("有一连接关闭!当前在线人数为" + getCurrUserCount(userId));}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息* @param session 可选的参数*/@OnMessagepublic void onMessage(String message, Session session) {CopyOnWriteArraySet<WebSocketService> webSocketSet = userwebSocketMap.get(userId);System.out.println("来自客户端" + userId + "的消息:" + message);//群发消息for (WebSocketService item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();continue;}}}/*** 发生错误时调用** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}/*** 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。** @param message* @throws IOException*/public void sendMessage(String message) throws IOException {System.out.println("服务端推送" + userId + "的消息:" + message);this.session.getAsyncRemote().sendText(message);}/*** 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。   我是在有代办消息时 调用此接口 向指定用户发送消息** @param message* @throws IOException*/public void sendMessage(String userId, String message) throws IOException {System.out.println("服务端推送" + userId + "的消息:" + message);CopyOnWriteArraySet<WebSocketService> webSocketSet = userwebSocketMap.get(userId);//群发消息for (WebSocketService item : webSocketSet) {try {item.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();continue;}}}public void sendOpenAllUserMessage(List<String> userIds, String message) {for (String userId : userIds) {CopyOnWriteArraySet<WebSocketService> webSocketSet = userwebSocketMap.get(userId);//群发消息for (WebSocketService item : webSocketSet) {try {item.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();continue;}}}}public boolean exitUser(String userId) {return userwebSocketMap.containsKey(userId);}public CopyOnWriteArraySet<WebSocketService> getUserSocketSet(String userId) {return userwebSocketMap.get(userId);}public void userCountIncrease(String userId) {if (count.containsKey(userId)) {count.put(userId, count.get(userId) + 1);}}public void userCountDecrement(String userId) {if (count.containsKey(userId)) {count.put(userId, count.get(userId) - 1);}}public void removeUserConunt(String userId) {count.remove(userId);}public Integer getCurrUserCount(String userId) {return count.get(userId);}private void initUserInfo(String userId) {CopyOnWriteArraySet<WebSocketService> webSocketServiceSet = new CopyOnWriteArraySet<WebSocketService>();webSocketServiceSet.add(this);userwebSocketMap.put(userId, webSocketServiceSet);count.put(userId, 1);}public List<String> getAllUser() {List<String> allUser = new LinkedList<>();Enumeration<String> keys = userwebSocketMap.keys();while (keys.hasMoreElements()) {String key = keys.nextElement();allUser.add(key);}return allUser;}}

4、

import com.lq.demo1.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;import java.util.List;@EnableScheduling
@Configuration
@Slf4j
public class TaskTimer {@Autowiredprivate WebSocketService webSocketService;@Scheduled(cron = "0/10 * * * * ?")public void cleanToken() {//10s推送一次List<String> allUser = webSocketService.getAllUser();//自己可以定义不同用户发送的信息,这里不做演示了webSocketService.sendOpenAllUserMessage(allUser, "告警!");}}

@EnableScheduling
在这里插入图片描述

然后把项目启动,打开在线调试websocket连接

路径格式为:ws://localhost:8081/websocket/1
在这里插入图片描述

在这里插入图片描述

成功展示,也可以搞多个窗口,发送内容一致

就先说到这\color{#008B8B}{ 就先说到这}就先说到这
在下Apollo\color{#008B8B}{在下Apollo}在下Apollo
一个爱分享Java、生活的小人物,\color{#008B8B}{一个爱分享Java、生活的小人物,}一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞!\color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!}咱们来日方长,有缘江湖再见,告辞!

在这里插入图片描述

相关文章:

springboot 接入websocket实现定时推送消息到客户端

目录说明代码实现说明 如标题&#xff0c;举例需求场景&#xff1a; 前端与后端websocket连接上后&#xff0c;多用户登录&#xff0c;后端根据不同用户定时发消息给前端用于展示 代码实现 1、 <dependency><groupId>org.springframework.boot</groupId>…...

虚拟机磁盘重新分区增加Docker磁盘空间

目录一、简介二、重新分区 挂载目录2.1 增加虚拟机硬盘空间2.2 重新分区2.3 格式化新分区2.4 挂载docker目录三、重新拉取一、简介 今天在使用docker pull 拉取镜像时&#xff0c;报了no such file or directory的信息&#xff0c;原来是Docker的磁盘空间满了 #查看Docker Roo…...

Java开发学习(四十八)----MyBatisPlus删除语句之逻辑删除

1、逻辑删除 接下来要讲解是删除中比较重要的一个操作&#xff0c;逻辑删除&#xff0c;先来分析下问题: 这是一个员工和其所签的合同表&#xff0c;关系是一个员工可以签多个合同&#xff0c;是一个一(员工)对多(合同)的表 员工ID为1的张业绩&#xff0c;总共签了三个合同&a…...

RabbitMq

一、四大核心概念生产者&#xff1a;产生数据发送消息的程序是生产者交换机&#xff1a;交换机是RabbitMQ非常重要的一个部件&#xff0c;一方面它接收来自生产者的消息&#xff0c;另一方面它将消息推送到队列中。交换机必须确切知道如何处理它接收到的消息&#xff0c;是将这…...

Qt学习笔记

文章目录一、C指针函数驼峰命名法、下划线命名法编程报错二、C三、Qt语法Qt历史、Qt应用Qt特色快捷键Qt类的族谱QWidgetQPushButtonQDebug对象树Qt窗口坐标信号和槽Qt自带的信号的槽自定义的信号和槽Qt4版本 vs Qt5版本 的connect写法函数指针解决重载问题拓展类型转换QString …...

洛谷——P1091 合唱队形

【题目描述】 n 位同学站成一排&#xff0c;音乐老师要请其中的 n−k 位同学出列&#xff0c;使得剩下的 k 位同学排成合唱队形。 合唱队形是指这样的一种队形&#xff1a;设 kk 位同学从左到右依次编号为 1,2, … ,k&#xff0c;他们的身高分别为​,​, … ,​&#xff0c;则…...

使用logstash把mysql同步到es,Kibana可视化查看

1&#xff1a;首先需要电脑本地有es环境&#xff0c;并且要牢记版本后&#xff0c;后续安装的logstash和Kibana一定要版本对应 查看es版本&#xff1a;http://localhost:9200/ 2&#xff1a;安装对应版本的logstash&#xff1a;找到自己对应ES版本&#xff0c;然后解压 Logst…...

Vue3.0 setup的使用及作用

目录开篇&#xff1a;1.什么是setup2.setup怎么使用3.setup中包含的生命周期函数3.setup相关参数4.setup特性总结总结开篇&#xff1a; 从vue2升级 vue3&#xff0c;vue3是可以兼容vue2。所以v3可以采用v2的选项式api&#xff0c;但是v2不能使用v3的组合式api&#xff0c;由于…...

Ubuntu18.04安装Vertica

目录下载安装包安装(Ubuntu18.04)配置 I/O Scheduler配置 TZSupport Tools配置 swapinessDisk ReadaheadEnabling chrony or ntpd自启动项错误处理后重装下载安装包 官网11.0版本或者10.0(deb)安装包可私信提供百度网盘链接&#xff1b; 安装(Ubuntu18.04) testvertica:~$ s…...

2.计算机基础-计算机网络面试题—基础知识、容器、面向对象、并发编程

本文目录如下&#xff1a;计算机基础-计算机网络 面试题一、基础知识简述 TCP 和 UDP 的区别&#xff1f;http与https的区别?Session 和 Cookie 有什么区别&#xff1f;URL是什么&#xff1f;由哪些部分组成&#xff1f;OSI 的 五层模型 都有哪些&#xff1f;get 和 post 请求…...

解决Mac 安装应用提示:xx已损坏,无法打开。 您应该将它移到废纸篓问题

许多新手mac 用户安装应用得时候会出现 “已损坏&#xff0c;无法打开。您应该将它移到废纸娄” 导致无法正常安装&#xff0c;其实应用软件b并没有损坏&#xff0c;只是系统安全设置&#xff0c;我们改如何解决呢&#xff1f; 1、开启允许任何来源 苹果已经取消了允许“任何…...

xpath注入[NPUCTF2020]ezlogin

[NPUCTF2020]ezlogin 打开界面 如果发现自己输入的信息由这样构成&#xff0c;可以往xpath注入上靠一下。 不管输入什么&#xff0c;很容易发现登陆就超时了&#xff0c;说明这里token是不断刷新的。 这样构造也是一样的目的都是为了闭合后面的&#xff0c;为啥有两个or呢 us…...

【Python学习笔记】22.Python3 数据结构

前言 本章节我们主要结合前面所学的知识点来介绍Python数据结构。 列表 Python中列表是可变的&#xff0c;这是它区别于字符串和元组的最重要的特点&#xff0c;一句话概括即&#xff1a;列表可以修改&#xff0c;而字符串和元组不能。 以下是 Python 中列表的方法&#xf…...

一文搞懂 什么是CPU上下文?为什么要切换?如何减少切换?

最近经常有小伙伴问到的一些问题&#xff0c;比较集中的是关于CPU切换. 实际用C/C&#xff0c;go开发&#xff0c;你会特别注意内存和CPU的使用情况&#xff0c;那些对于CPU使用情况特别关注&#xff0c;或者性能特别关注的朋友可以看看这篇文章&#xff0c;相信看完结尾的示例…...

【Python】Python学习笔记(二)基本输入输出

Python娘来源&#xff1a;https://next.rikunabi.com/tech/docs/ct_s03600.jsp?p002412 目录print()函数不进行自动换行的print()函数打印输出多个字符串只进行换行input()函数使用format方法格式化字符串字符串与数值转换字符串转换为数值数值转换为字符串总结参考资料print(…...

LeetCode刷题系列 -- 724. 寻找数组的中心下标

给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为在下标的左侧不存在元素。这一点对于…...

Linux编辑器vim

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 目录 前言 正文 vim常用方式 进入vim 退出vim vim基本模式及模式功能 命令模式 插入模式 底行模式 替换模式 视图模式 配置vim 自己配置vim 自动化配置…...

基于“python+”潮汐、风驱动循环、风暴潮等海洋水动力模拟

查看原文>>>基于“python”潮汐、风驱动循环、风暴潮等海洋水动力模拟ADCIRC是新一代海洋水动力计算模型&#xff0c;它采用了非结构三角形网格广义波动连续方程的设计&#xff0c;在提高计算精确度的同时还减小了计算时间。被广泛应用于&#xff1a;模拟潮汐和风驱动…...

《Terraform 101 从入门到实践》 第二章 Providers插件管理

《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新&#xff0c;书中的示例代码也是放在GitHub上&#xff0c;方便大家参考查看。 不怕出身低&#xff0c;行行出状元。 插件 Terraform可以对多种平台的多种资源进行管理&#xff0c;这个是通过…...

03- pandas 数据库可视化 (机器学习)

pandas库的亮点: 一个快速、高效的DataFrame对象&#xff0c;用于数据操作和综合索引&#xff1b;用于在内存数据结构和不同格式之间读写数据的工具&#xff1a;CSV和文本文件、Microsoft Excel、SQL数据库和快速HDF 5格式&#xff1b;智能数据对齐和丢失数据的综合处理&#…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...