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;}

然后就可以连接了

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

springboot老年康复中心—计算机毕业设计源码27406

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出法&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤,采用Springboo框架建设老年康复中心信息管理系统。…

vue2基础系列教程之v-model及面试高频问题

v-model是表单组件里面的核心知识点&#xff0c;这个指令给我们写表单业务带来了很大的方便。 元素标签上的 v-model 指令用于双向绑定数据,它是一个语法糖&#xff0c;可以用于代替 v-bind:value 和 input 例如&#xff1a;<input v-model"message" placeholder…

用户洞察|从被动养老到主动康养,新康养人群的幸福生活新思路

图源&#xff1a;NewAgingPro团队访谈摄影 全龄康养&#xff0c;大势所趋 作者 | NewAgingPro团队 排版 | Lucy 前言 本文将从我国银发经济发展背景出发&#xff0c;结合AgeClub旗下的NewAgingPro新老年商业研究团队持续多年的用户需求调研及访谈分析&#xff0c;深度洞察…

OpenCV运动分析和目标跟踪(1)累积操作函数accumulate()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将一个图像添加到累积图像中。 该函数将 src 或其部分元素添加到 dst 中&#xff1a; dst ( x , y ) ← dst ( x , y ) src ( x , y ) if mask…

SQL Server小技巧之遍历日期

使用背景 一般项目中会遇到&#xff0c;求每日的日报这种&#xff0c;以及计算2个日期内的工作日&#xff0c;或者休息日可能会用到&#xff0c;计算休息日可以用额外的一个字段用来标记当前日期是否是休息日 遍历方式一 DECLARE StartDate DATE 2023-01-01, EndDate DATE …

SSM基于响应式的网上书店系统—计算机毕业设计源码27119

摘 要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理系统的实施在技术上已逐步成熟。管理系统是一个不断发展的新型学科&#xff0c;本文主要通过对响应式的网上书店系统的功能性需求分析&#xff0c;对系统的安全性和可扩展性进行了非功能性需求分析。在详细的…

【C++】详细介绍类的相关语法概念,this,默认成员函数,static,友元,内部类

目录 1. 类的基本概念 1.1 类的语法 1.2 类的访问限定符 1.3 类的两种定义方式 1.4 类的作用域 1.5 类的实例化 1.6 类对象的大小 2. this指针 2.1 概念 2.2 特性 2.3 面试题 3. 类的六个默认成员函数 3.1 构造函数 3.1.1 特性 3.1.2 默认构造函数 3.1.3 函数…

高效数据移动指南 | 如何快速实现数据库 MySQL 到 MongoDB 的数据同步?

在现代企业中&#xff0c;数据无处不在&#xff0c;贯穿于各个业务环节和系统之间。无论是跨系统的数据集成、多地域的数据协同&#xff0c;还是实时应用的数据同步&#xff0c;数据的一致性和及时性都至关重要。在数字化转型的过程中&#xff0c;如何确保不同系统、地域、设备…

OCR2.0--General OCR Theory

引领光学字符识别&#xff08;OCR&#xff09;的新篇章 引言&#xff1a;OCR技术进化的必要性 光学字符识别&#xff08;OCR&#xff09;是一项广泛应用的技术&#xff0c;它能够从图像中提取字符并将其转换为可编辑格式。虽然OCR-1.0在过去取得了广泛应用&#xff0c;但传统…

水滴式多功能粉碎机:粉碎中草药的好帮手

水滴式中草药粉碎机&#xff0c;顾名思义&#xff0c;其设计灵感源自自然界中水滴的柔和与力量。它摒弃了传统粉碎机粗犷的粉碎方式&#xff0c;采用低速研磨技术&#xff0c;模拟水滴穿透岩石的细腻与持久&#xff0c;对中草药进行温和而深入的粉碎。这种技术不仅保留了药材中…

Redis集群_cluster

cluster集群 cluster翻译就是集群&#xff0c;所以cluster集群也叫做redis集群相比于哨兵模式&#xff0c;cluster集群能支持扩容&#xff0c;并且无需额外的节点来监控状态&#xff0c;所以使用这种模式集群的系统会用的更多些redis cluster采用的是去中心化网络拓扑架构&…

2024.9.14(RC和RS)

一、replicationcontroller &#xff08;RC&#xff09; 1、更改镜像站 [rootk8s-master ~]# vim /etc/docker/daemon.json {"registry-mirrors": ["https://do.nark.eu.org","https://dc.j8.work","https://docker.m.daocloud.io",&…

探索UWB技术的独特优势:实现高精度定位

UWB定位技术是一种利用无线信号进行精确位置定位的技术&#xff0c;它利用超宽带无线电信号通过测量信号的到达时间、相位差和信号能量等参数来确定物体的精确位置。 UWB定位技术具有多种优势&#xff0c;首先&#xff0c;它具有较高的定位精度&#xff0c;可实现毫米级的精确…

哈工大“计算机设计与实践”(cpu)处理器实验设计报告

哈工大“计算机设计与实践”&#xff08;cpu&#xff09;处理器实验设计报告 【哈工大“计算机设计与实践”&#xff08;cpu&#xff09;处理器实验设计报告】 在计算机科学领域&#xff0c;CPU&#xff08;中央处理器&#xff09;是计算机系统的核心部件&#xff0c;负责执行指…

性能诊断的方法(四):自下而上的资源诊断方法和发散的异常信息诊断方法

关于性能诊断的方法&#xff0c;我们可以按照“问题现象—直接原因—问题根源”这样一个思路去归纳。我们先从问题的现象去入手&#xff0c;包括时间的分析、资源的分析和异常信息的分析。接下来再去分析产生问题现象的直接原因是什么&#xff0c;这里我们归纳了自上而下的资源…

C语言 13 指针

指针可以说是整个 C 语言中最难以理解的部分了。 什么是指针 还记得在前面谈到的通过函数交换两个变量的值吗&#xff1f; #include <stdio.h>void swap(int, int);int main() {int a 10, b 20;swap(a, b);printf("a %d, b %d", a, b); }void swap(int …

Python编码系列—Python建造者模式:构建复杂对象的优雅之道

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

传知代码-融合经典与创新的图像分类新途径

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 在当前的深度学习领域&#xff0c;构建兼具高性能与灵活性的卷积神经网络&#xff08;CNN&#xff09;已成为计算机视觉研究的核心课题。本文介绍了一种全新的卷积神经网络架构&#xff0c;该网络巧妙地结合…

OZON电子产品大幅增长,OZON跨境PS5销量激增

Top1 存储卡 Карта памяти Canvas Select Plus 128 ГБ 商品id&#xff1a;1548303593 月销量&#xff1a;2131 欢迎各位卖家朋友点击这里&#xff1a; &#x1f449; D。DDqbt。COm/74rD 免费体验 随着智能手机和平板电脑的普及&#xff0c;用户对于存储空…

vite + vue3 + ts 移动端开箱即用现代开发模板

中文 | English SouthernWind https://blog.csdn.net/nanchen_J?typeblog sw-template vite vue3 ts 移动端开箱即用现代开发模板 特点 &#x1f436; Vite 的Vue3 的文件路由布局系统Mock 后续支持Api 自动引入组件自动引入VueUse 支持TypeScript 的Tailwind css 的暗…