SpringBoot整合WebSocket实现消息推送或聊天功能示例
最近在做一个功能,就是需要实时给用户推送消息,所以就需要用到 websocket
springboot 接入 websocket 非常简单,只需要下面几个配置即可
pom 文件
<!-- spring-boot-web启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring WebSocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- lombok插件 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
application.yml
server:port: 1001logging:config: classpath:logback-spring.xmlspring:profiles:active: dev
config 文件,我之前参考的别人的博客就是没有这个,导致怎么都请求不了,这个要注意
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author Sakura* @date 2024/9/13 14:02* 开启websocket支持*/
@Configuration
public class WebSocketConfig {// 使用boot内置tomcat时需要注入此bean@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
工具类
import lombok.extern.log4j.Log4j2;import javax.websocket.Session;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;/*** @author Sakura* @date 2024/9/13 11:40*/
@Log4j2
public class WebsocketUtil {private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<>();/*** 添加session*/public static void addSession(String userId, Session session){// 一个用户只允许一个session链接ONLINE_SESSION.putIfAbsent(userId, session);log.info("User [{}] connected. Total online users: {}", userId, ONLINE_SESSION.size());}/*** 移除session*/public static void removeSession(String userId){ONLINE_SESSION.remove(userId);log.info("User [{}] disconnected. Total online users: {}", userId, ONLINE_SESSION.size());}/*** 给单个用户推送消息*/public static void sendMessage(String userId, String message){Session session = ONLINE_SESSION.get(userId);if(session == null){log.warn("Session for user [{}] not found", userId);return;}sendMessage(session, message);}public static void sendMessage(Session session, String message) {if (session != null) {session.getAsyncRemote().sendText(message);}}/*** 给所有用户发消息*/public static void sendMessageForAll(String message) {ONLINE_SESSION.forEach((userId, session) -> {CompletableFuture.runAsync(() -> sendMessage(session, message)).exceptionally(ex -> {log.error("Failed to send message to user [{}]: {}", userId, ex.getMessage());return null;});});}/*** 给指定的多个用户推送消息*/public static void sendMessageForUsers(Set<String> userIds, String message) {userIds.forEach(userId -> {Session session = ONLINE_SESSION.get(userId);if (session == null) {log.warn("Session for user [{}] not found", userId);return;}CompletableFuture.runAsync(() -> sendMessage(session, message)).exceptionally(ex -> {log.error("Failed to send message to user [{}]: {}", userId, ex.getMessage());return null;});});}}
WebsocketController
import com.yike.websocket.util.WebsocketUtil;
import lombok.extern.java.Log;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;/*** @author Sakura* @date 2024/9/13 11:41*/
@Component
@ServerEndpoint(value = "/chat/{userId}")
@Log
public class WebsocketController {/*** 连接事件,加入注解* @param userId* @param session*/@OnOpenpublic void onOpen(@PathParam(value = "userId") String userId, Session session) {log.info("WebSocket连接成功,用户ID: " + userId);// 添加到session的映射关系中WebsocketUtil.addSession(userId, session);// 广播通知,某用户上线了
// WebsocketUtil.sendMessageForAll(message);}/*** 连接事件,加入注解* 用户断开链接** @param userId* @param session*/@OnClosepublic void onClose(@PathParam(value = "userId") String userId, Session session) {log.info("WebSocket连接断开,用户ID: " + userId);// 删除映射关系WebsocketUtil.removeSession(userId);// 广播通知,用户下线了
// WebsocketUtil.sendMessageForAll(message);}/*** 当接收到用户上传的消息** @param userId* @param session*/@OnMessagepublic void onMessage(@PathParam(value = "userId") String userId, Session session, String message) {log.info("用户ID: " + userId + " 发送消息: " + message);// 直接广播
// WebsocketUtil.sendMessageForAll(msg);}/*** 处理用户活连接异常** @param session* @param throwable*/@OnErrorpublic void onError(Session session, Throwable throwable) {log.info("用户异常断开链接,原因: " + throwable.getMessage());try {session.close();} catch (IOException e) {e.printStackTrace();}throwable.printStackTrace();}
}
然后我们加一个测试用的发消息接口
import com.yike.websocket.util.WebsocketUtil;
import lombok.extern.java.Log;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.Set;/*** @author Sakura* @date 2024/9/13 13:38*/
@RestController
@RequestMapping("/msg")
@Log
public class MsgController {@PostMapping("/send")public void send(@RequestParam("id") String id, @RequestParam("message") String message) {log.info("发送消息给:" + id + "-" + message);WebsocketUtil.sendMessage(id, message);}@PostMapping("/sendAll")public void sendAll(@RequestParam("message") String message) {log.info("群发消息:" + message);WebsocketUtil.sendMessageForAll(message);}@PostMapping("/sendUserList")public void sendAll(@RequestParam("userIds") Set<String> userIds, @RequestParam("message") String message) {log.info("发送多人消息:" + userIds.toString() + message);WebsocketUtil.sendMessageForAll(message);}
}
接下来我们开始测试,这里我用的 apipost,听说 postman 也可以连接 websocket 但是我试了下没找到在哪里
在 apipost 里面选择新建Websocket就可以了

刚才我们在 WebsocketController 里面写的地址是 /chat/{userId}

所以这里就填 ws://127.0.0.1:1000/chat/123, 后面那个123就是用户唯一标识,通常情况就是客户端登录成功后拿到用户ID了然后再通过这个ID来建立 websocket 连接
我们点击那个连接,可以看到提示连接成功了

看一下控制台,这里显示两个用户是因为我建立了两个连接方面后面测试,大家也是一样,换一下后面的 userId 就可以

下面我们通过这几个接口测试给客户端发消息

首先是给单个用户发消息,我们给用户 123 发消息,这里用 JSON 字符串是为了分辨不同的消息类型,让客户端知道要做什么,大家可以随便定义,反正就是个字符串类型

可以看到控制台提示发送成功了

我们去看下 123 的控制台,可以看到拿到消息了

然后群发消息 “大家好”

看下一 456,收到了

123 也收到了

给多个用户发消息的也是一样的

我这里因为只是服务端给客户端推送消息,如果大家要做聊天功能的话就需要自己通过这三个接口来写业务逻辑实现
客户端发消息到服务端

看一下控制台可以看到已经收到消息了

大家要是想做聊天工具的话就需要在下面这个接口里面加逻辑,比如客户端发 JSON 格式的字符串,然后里面指定好用户和消息内容这些就可以,当然大家最好做好认证这些,确保消息是用户自己发出的

普通的 springboot 项目上面那些就够了,但是因为我的项目是 springcloud 的,我这边想的是把这个服务单独出来,然后其它服务通过 openfeign 来调这个服务给客户端推送消息,所以这里面就整合了nacos 和 gateway,然后前端通过公共的域名来访问,比如 wss://www.sakura.com/api-websocket/chat/2(注意:因为我域名是HTTPS协议的,所以连接的时候要用 wss)
好了接下来我只说重点的配置,其它的不动的大家不懂可以直接问我即可
首先是启动文件,因为我这是个单独的服务,所以不需要连接数据库这些,所以要加上 exclude= {DataSourceAutoConfiguration.class}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;/*** @author Sakura* @date 2024/9/13 11:43*/
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class WebSocketApplication {public static void main(String[] args) {SpringApplication.run(WebSocketApplication.class, args);}}
然后就是gateway的路由配置

feign 接口大家根据自己的项目写就好

最后就是域名的 nginx 配置,我们在里面加上这个
location /api-websocket/ {proxy_pass http://localhost:1001/;# WebSocket 相关的头部配置proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;# 避免 WebSocket 超时断开proxy_read_timeout 3600;proxy_send_timeout 3600;}
然后就可以连接了

相关文章:
SpringBoot整合WebSocket实现消息推送或聊天功能示例
最近在做一个功能,就是需要实时给用户推送消息,所以就需要用到 websocket springboot 接入 websocket 非常简单,只需要下面几个配置即可 pom 文件 <!-- spring-boot-web启动器 --><dependency><groupId>org.springframewo…...
使用 QEMU 模拟器运行 FreeRTOS 实时操作系统
文章目录 QEMU 官网QEMU 文档QEMU 简介QEMU 安装QEMU 命令启动虚拟机串口控制台监控命令行 FreeRTOS安装编译工具FreeRTOS 源码RISC-V-Qemu-virt_GCC 示例编译 RISC-V-Qemu-virt_GCC启动虚拟机运行 FreeRTOS QEMU 官网 https://www.qemu.org/ QEMU 文档 https://www.qemu.or…...
Oracle EBS中AR模块的财务流程概览
应收账款 (AR) 模块是Oracle E-Business Suite (EBS) 中另一个重要的财务管理模块,主要用于管理企业销售过程中的账款回收。下面是AR模块中的一些关键财务流程及其详细说明: 1. 销售订单管理 创建销售订单:当客户下单时,销售人员…...
Minitab 的直方图结果分析解释
Minitab 的直方图结果分析解释 步骤 1:评估关键特征 检查分布的尖峰和散布。评估样本数量对直方图外观的影响。 标识尖峰(即,条的最高聚类): 尖峰表示样本中最常见的值。评估样本的散布以了解数据的变异程度。例如…...
AgentRE:用智能体框架提升知识图谱构建效果,重点是开源!
发布时间:2024 年 09 月 13 日 Agent应用 AgentRE: An Agent-Based Framework for Navigating Complex Information Landscapes in Relation Extraction 在复杂场景中,关系抽取 (RE) 因关系类型多样和实体间关系模糊而挑战重重,影响了传统 “…...
力扣题解2390
大家好,欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述(中等): 从字符串中移除星号 给你一个包含若干星号 * 的字符串 s 。 在一步操作中,你可以: 选中 s 中的一个星号。 移除星号…...
用Python获取PDF页面的大小、方向和旋转角度
在文档管理和自动化领域,了解PDF文档的内在属性(如页面大小、方向和旋转角度)对于确保一致的文档处理和布局保真度至关重要。这些属性在内容重用、归档以及PDF无缝集成到网络环境或其他数字工作流程中起着关键作用,因为它们直接影…...
【即时通讯】轮询方式实现
技术栈 LayUI、jQuery实现前端效果。django4.2、django-ninja实现后端接口。 代码仓 - 后端 代码仓 - 前端 实现功能 首次访问页面并发送消息时需要设置昵称发送内容为空时要提示用户不能发送空消息前端定时获取消息,然后展示在页面上。 效果展示 首次发送需要…...
Flock 明牌空投教程
FLock 旨在为人工智能构建一个去中心化的隐私保护解决方案。FLock提出了一项名为联合学习区块(简称 FLocks)的研究计划,该计划使用区块链作为数据持有者之间的协调平台来进行机器学习,同时数据保持本地和隐私。通过用区块链取代收…...
项目内部调用的远程接口开发
编写一个项目内部调用的远程接口通常是为了在分布式系统或者微服务架构中,实现各个服务之间的通信和数据交换。这样的远程接口专门用于服务之间的调用,而不是直接暴露给外部用户或前端。 项目内部的远程接口统一放在api工程 首先进入api编写接口&#x…...
影响IP代理池稳定性的因素有哪些?
IP代理池在提供网络服务时,稳定性是一项决定性指标。多个外部和内部因素可能会影响这个稳定性,因此深入理解这些影响因素,可以帮助优化IP代理池的性能与服务质量。 1. IP来源质量 纯净度与使用频次:优质的IP来源常常被描述为纯净…...
基于Prometheus和Grafana的现代服务器监控体系构建
构建一个基于 Prometheus 和 Grafana 的现代服务器监控体系涉及多个步骤。以下是大体的流程和步骤说明: 1. Prometheus 监控系统 Prometheus 是一个开源的系统监控和报警工具,专门设计用于抓取时间序列数据。 1.1 Prometheus 的安装 Docker 安装 Prom…...
原生 input 中的 “type=file“ 上传文件
目标:实现文件上传功能 原型图: HTML部分: <div class"invoice-item"><div class"invoice-title">增值税专用发票</div><div class"invoice-box"><el-form-item label"标准…...
【Unity新闻】Unity的产品命名变化
快速回顾一下Unity产品命名的调整。 在2023年 Unity就宣布版本命名的变化,将使用Unity 6作为最新版本的命名。 具体的规则,在论坛里进行了说明。 以后正式的LTS版本就是Unity 6,将在2024年末发布。 而不管是之前的Runtime费还是今天的费用…...
《PostMan(一):配置全局令牌》
文章目录 一、配置全局token1、设置2、添加全局3、添加全局变量名称4、选中全局,并查看5、添加赋值脚本6、配置令牌取值7、即可成功获取用户信息 一、配置全局token 1、设置 2、添加全局 3、添加全局变量名称 4、选中全局,并查看 5、添加赋值脚本 // 把…...
如何理解Configurational entropy
Configurational entropy 是热力学和统计力学中的一个重要概念,它描述的是系统中由于其微观状态排列(即配置)导致的不确定性或混乱程度。不同于热力学中的热熵(thermal entropy),它特指那些与系统中的粒子、…...
H5端接入萤石监控
官方文档 EZOPEN协议 下滑至-平台架构 web/h5端使用文档 <template><div :id"video-container${index}${index2}" class"w-full bg-black"></div> </template><script>export default {data() {return {EZVIZAToken:…...
SSD1306 OLED显示屏驱动方案简介
SSD1306是一种常见的单色OLED(有机发光二极管)显示屏驱动芯片。以下从它的基本特点、工作原理和应用领域进行详细介绍: 一、基本特点 1. 尺寸与分辨率: SSD1306芯片支持多种尺寸的OLED显示屏,常见的有0.96寸、1.3寸等。不同尺寸的屏幕具有不…...
React18快速入门
需要先安装并配置React相关的工具和插件 下载安装Node.js,这里以MacOS Node.js v22.6.0为例 终端命令行检查是否安装成功 node -v npm -vNode.js快速入门 npm设置镜像源 #设置为阿里镜像源 npm config set registry https://registry.npmmirror.com #查看是否生…...
Day11笔记-字典基本使用系统功能字典推导式
二、字典【重点掌握】 1.概念 列表和元组的使用缺点:当存储的数据要动态添加、删除的时候,我们一般使用列表,但是列表有时会遇到一些麻烦,定位元素比较麻烦 # 一个列表/元组保存5个学生的成绩, score_list [66,100,70,78,99] sc…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
