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

在 Spring Boot 中实现 WebSockets

什么是 WebSockets?

WebSockets 是一种基于 TCP 的全双工通信协议,允许客户端和服务器之间建立持久的双向连接,用于实时数据交换。相较于传统的 HTTP 请求-响应模型,WebSockets 提供了低延迟、高效率的通信方式,特别适合需要实时更新的应用场景,如聊天应用、实时通知、在线游戏和股票价格更新。

核心特点
  1. 全双工通信:客户端和服务器可同时发送和接收数据。
  2. 持久连接:一次握手后,连接保持开放,减少重复建立连接的开销。
  3. 低延迟:适合实时性要求高的场景。
  4. 轻量协议:基于 HTTP 协议升级(通过 Upgrade 头),数据帧开销小。
  5. 跨平台支持:现代浏览器和服务器均支持 WebSockets。
工作原理
  1. 握手:客户端通过 HTTP 发送 WebSocket 握手请求(GET /ws HTTP/1.1Upgrade: websocket 头),服务器响应 101 Switching Protocols
  2. 连接建立:双方建立 WebSocket 连接,使用 ws://wss://(加密)协议。
  3. 数据交换:通过文本或二进制帧传输数据,连接保持开放直到一方关闭。
  4. 关闭:发送关闭帧,断开连接。
与 HTTP 和 AJAX 的对比
  • HTTP:单向、请求-响应模式,适合静态内容。
  • AJAX:通过轮询模拟实时性,增加服务器负载。
  • WebSockets:持久连接,低延迟,适合动态交互。
应用场景
  • 实时聊天(如 WhatsApp)。
  • 实时通知(如新消息提醒)。
  • 在线协作工具(如 Google Docs)。
  • 金融数据流(如股票价格)。
  • 多人游戏。
挑战
  • 资源消耗:持久连接占用服务器资源。
  • 复杂性:需要处理连接断开、重连等。
  • 安全性:需防止未授权访问(参考你的 Spring Security 查询)。
  • 集成:需与分页、Swagger、ActiveMQ、Spring Profiles、Spring Batch、FreeMarker、热加载、ThreadLocal、Actuator 安全性、CSRF、异常处理等协调。

Spring Boot 通过 Spring WebSocket 和 STOMP(Simple Text Oriented Messaging Protocol)简化 WebSocket 实现。以下是在 Spring Boot 中实现 WebSockets 的步骤,结合你的先前查询(分页、Swagger、ActiveMQ、Spring Profiles、Spring Security、Spring Batch、FreeMarker、热加载、ThreadLocal、Actuator 安全性、CSRF、异常处理)。

1. 环境搭建
  1. 添加依赖pom.xml):

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
    <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.2.0</version>
    </dependency>
    
  2. 配置 application.yml

    spring:profiles:active: devfreemarker:template-loader-path: classpath:/templates/suffix: .ftlcache: falseactivemq:broker-url: tcp://localhost:61616user: adminpassword: admin
    server:port: 8081
    springdoc:api-docs:path: /api-docsswagger-ui:path: /swagger-ui.html
    
2. 实现 WebSocket 聊天应用

以下是一个简单的实时聊天应用示例,使用 STOMP over WebSocket。

  1. WebSocket 配置

    package com.example.demo.config;import org.springframework.context.annotation.Configuration;
    import org.springframework.messaging.simp.config.MessageBrokerRegistry;
    import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
    import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
    import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;@Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/topic"); // 广播消息config.setApplicationDestinationPrefixes("/app"); // 客户端发送消息前缀}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/chat").withSockJS(); // WebSocket 端点,兼容 SockJS}
    }
    
  2. 消息控制器

    package com.example.demo.controller;import org.springframework.messaging.handler.annotation.MessageMapping;
    import org.springframework.messaging.handler.annotation.SendTo;
    import org.springframework.stereotype.Controller;@Controller
    public class ChatController {@MessageMapping("/sendMessage")@SendTo("/topic/messages")public ChatMessage sendMessage(ChatMessage message) {return message; // 广播消息}
    }
    
  3. 消息实体

    package com.example.demo.controller;public class ChatMessage {private String content;private String sender;// Getters and Setterspublic String getContent() { return content; }public void setContent(String content) { this.content = content; }public String getSender() { return sender; }public void setSender(String sender) { this.sender = sender; }
    }
    
  4. FreeMarker 聊天页面src/main/resources/templates/chat.ftl):

    <!DOCTYPE html>
    <html>
    <head><title>实时聊天</title><script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.6.1/sockjs.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script><script>var stompClient = null;function connect() {var socket = new SockJS('/chat');stompClient = Stomp.over(socket);stompClient.connect({}, function(frame) {stompClient.subscribe('/topic/messages', function(message) {var msg = JSON.parse(message.body);document.getElementById('messages').innerHTML += '<p>' + msg.sender + ': ' + msg.content + '</p>';});});}function sendMessage() {var content = document.getElementById('content').value;var sender = document.getElementById('sender').value;stompClient.send('/app/sendMessage', {}, JSON.stringify({'content': content,'sender': sender}));document.getElementById('content').value = '';}window.onload = connect;</script>
    </head>
    <body><h1>实时聊天</h1><div><label>用户名: <input id="sender" type="text" value="User"/></label><label>消息: <input id="content" type="text"/></label><button onclick="sendMessage()">发送</button></div><div id="messages"></div>
    </body>
    </html>
    
  5. 控制器

    package com.example.demo.controller;import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;@Controller
    public class WebSocketController {@GetMapping("/chat")public String chat() {return "chat";}
    }
    
  6. 运行验证

    • 启动应用:mvn spring-boot:run
    • 访问 http://localhost:8081/chat,打开多个浏览器窗口。
    • 输入用户名和消息,发送后所有窗口实时显示消息。
3. 与先前查询集成

结合你的查询(分页、Swagger、ActiveMQ、Spring Profiles、Spring Security、Spring Batch、FreeMarker、热加载、ThreadLocal、Actuator 安全性、CSRF、异常处理):

  1. 分页与排序

    • 实时显示分页用户数据:
      @MessageMapping("/fetchUsers")
      @SendTo("/topic/users")
      public Page<User> fetchUsers(@Payload UserFilter filter) {return userService.searchUsers(filter.getName(), filter.getPage(), filter.getSize(), "id", "asc");
      }
      
      public class UserFilter {private String name;private int page;private int size;// Getters and Setters
      }
      
  2. Swagger

    • 文档化 REST API,非 WebSocket:
      @Operation(summary = "获取用户列表")
      @GetMapping("/api/users")
      public Page<User> getUsers(@RequestParam String name, @RequestParam int page, @RequestParam int size) {return userService.searchUsers(name, page, size, "id", "asc");
      }
      
  3. ActiveMQ

    • 记录聊天消息:
      @Controller
      public class ChatController {@Autowiredprivate JmsTemplate jmsTemplate;@MessageMapping("/sendMessage")@SendTo("/topic/messages")public ChatMessage sendMessage(ChatMessage message) {jmsTemplate.convertAndSend("chat-log", message.getSender() + ": " + message.getContent());return message;}
      }
      
  4. Spring Profiles

    • 配置开发/生产环境:
      # application-dev.yml
      spring:freemarker:cache: false
      logging:level:root: DEBUG
      
      # application-prod.yml
      spring:freemarker:cache: true
      
  5. Spring Security

    • 保护 WebSocket 连接:
      @Configuration
      public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.requestMatchers("/chat").authenticated().anyRequest().permitAll()).formLogin().and().csrf().ignoringRequestMatchers("/chat"); // WebSocket 端点禁用 CSRFreturn http.build();}
      }
      
  6. Spring Batch

    • 批量推送用户数据:
      @Component
      public class BatchConfig {@Beanpublic Step pushUsers(@Autowired SimpMessagingTemplate messagingTemplate) {return stepBuilderFactory.get("pushUsers").<User, User>chunk(10).reader(reader()).processor(user -> {messagingTemplate.convertAndSend("/topic/users", user);return user;}).writer(writer()).build();}
      }
      
  7. FreeMarker

    • 已使用 FreeMarker 渲染聊天页面。
  8. 热加载

    • 启用 DevTools:
      spring:devtools:restart:enabled: true
      
  9. ThreadLocal

    • 清理 WebSocket 上下文:
      @Service
      public class UserService {private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();public Page<User> searchUsers(...) {try {CONTEXT.set("WS-" + Thread.currentThread().getName());// 逻辑} finally {CONTEXT.remove();}}
      }
      
  10. Actuator 安全性

    • 保护 /actuator/**,允许 /actuator/health
  11. CSRF

    • WebSocket 端点禁用 CSRF(见 SecurityConfig)。
  12. 异常处理

    • 处理 WebSocket 异常:
      @ControllerAdvice
      public class WebSocketExceptionHandler {@ExceptionHandler(MessagingException.class)public void handleMessagingException(MessagingException ex, SimpMessageHeaderAccessor headerAccessor) {headerAccessor.getSessionAttributes().put("error", ex.getMessage());}
      }
      
4. 运行验证
  • 开发环境

    java -jar demo.jar --spring.profiles.active=dev
    
    • 访问 http://localhost:8081/chat,登录后发送消息,验证实时性。
    • 检查 ActiveMQ chat-log 队列。
  • 生产环境

    java -jar demo.jar --spring.profiles.active=prod
    
    • 确认安全性和模板缓存。

原理与性能

原理
  • WebSocket 协议:基于 TCP,使用 HTTP 握手升级。
  • STOMP:在 WebSocket 上添加消息路由,支持订阅/发布。
  • Spring WebSocket:管理连接、消息路由和广播。
性能
  • 连接建立:50ms(单用户)。
  • 消息传输:1-2ms/消息。
  • 并发:1000 用户,延迟 <10ms(8 核 CPU,16GB 内存)。
测试
@Test
public void testWebSocketPerformance() {WebSocketClient client = new StandardWebSocketClient();client.doHandshake(new TextWebSocketHandler(), "ws://localhost:8081/chat");// 测试消息发送
}

常见问题

  1. 连接失败

    • 问题:WebSocket 握手失败。
    • 解决:检查端点路径,禁用防火墙。
  2. 消息丢失

    • 问题:客户端未收到消息。
    • 解决:确认订阅 /topic/messages
  3. ThreadLocal 泄漏

    • 问题:/actuator/threaddump 显示泄漏。
    • 解决:清理 ThreadLocal。

总结

WebSockets 提供实时双向通信,Spring Boot 通过 STOMP 简化实现。示例展示了聊天应用及与分页、Swagger、ActiveMQ 等集成。针对你的查询(ThreadLocal、Actuator、热加载、CSRF),通过清理、Security 和 DevTools 解决。

相关文章:

在 Spring Boot 中实现 WebSockets

什么是 WebSockets&#xff1f; WebSockets 是一种基于 TCP 的全双工通信协议&#xff0c;允许客户端和服务器之间建立持久的双向连接&#xff0c;用于实时数据交换。相较于传统的 HTTP 请求-响应模型&#xff0c;WebSockets 提供了低延迟、高效率的通信方式&#xff0c;特别适…...

stone 3d v3.3.0版本发布,含时间线和连接器等新功能

1.新加了时间线&#xff08;timeline&#xff09;编辑器&#xff0c;可以类似blender一样给对象制作动画 2.新加了度量&#xff08;metrics&#xff09;系统&#xff0c;通过scene对象检测器中的useMetrics属性来启用或禁用&#xff0c;启用时所选物体将显示三维度量数据 新加了…...

.whl文件

本文主要介绍了.whl文件的定义&#xff0c;怎么安装.whl文件&#xff08;离线&#xff0c;在线&#xff09;。 怎么查看cuda的版本&#xff0c;以及如何安装相应版本的cuda&#xff08;本地电脑&#xff0c;超算上&#xff09; 以及如何创建.whl文件 .whl文件的定义 Document…...

Git命令行中vim的操作

Git命令行用vim打开文件&#xff0c;或者用其他git命令打开了文件&#xff0c;需要编辑和保存文件等&#xff0c;有些命令表情奇怪&#xff0c;往往容易忘记这些命令。记录下。 下面这篇比较实用和简练&#xff1a; gitvim编辑文件命令 • Worktile社区https://worktile.com/…...

C#初级知识总结

一、什么是CIL 1.CIL(Common Intermidate Language)是指.Net的公共中间语言&#xff0c;它是一种编程语言。 .Net框架的各种语言在编译时都会编译成同一种中间语言&#xff08;CIL&#xff09;&#xff0c;之后程序运行的时候CIL会被JIT(Just In Time)转换为二进制语言&#xf…...

使用 AI Agent 改善师生互动的设计文档

使用 AI Agent 改善师生互动的设计文档 一、引言 1.1 研究背景 当前教育领域的师生互动存在诸多挑战&#xff0c;如教师负担过重、学生个体差异大导致难以满足所有人的需求&#xff0c;以及信息传递延迟等问题。引入AI-Agent能够有效缓解这些问题&#xff0c;通过自动化手段协…...

Linux学习笔记之环境变量

写这篇博客的目的主要是因为本人学习动静态库时&#xff0c;用到了环境变量的知识&#xff0c;发现略有遗忘&#xff0c;因此回顾复习&#xff0c;整理成博客。 一、环境变量是什么 Linux环境变量是存储系统或程序运行时配置信息的特殊变量&#xff0c;用于为程序提供配置参数…...

16:00开始面试,16:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到4月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…...

深度解析云计算:概念、优势与分类全览

以下是对云计算概念、优点和分类更详细的介绍&#xff1a; 一、云计算的概念 云计算是一种通过互联网提供计算服务的模式&#xff0c;它基于虚拟化、分布式计算、网络存储等一系列先进技术&#xff0c;将计算资源进行整合和管理&#xff0c;形成一个庞大的资源池。这些资源包…...

私钥连接服务器(已经有服务器私钥

前言&#xff1a;假设我们已经有了服务器的私钥&#xff0c;我们怎么配置呢&#xff1f; 下面我会从vsc的配置角度来写 ✅ 步骤一&#xff1a;准备工作 安装 VS Code&#xff08;如果还没装&#xff09; &#x1f449; https://code.visualstudio.com/ 安装插件&#xff1a;Re…...

学员答题pk知识竞赛小程序怎么做

制作学员答题PK知识竞赛小程序&#xff0c;主要有以下步骤&#xff1a; 一、规划设计 明确需求&#xff1a;确定小程序的使用场景是校园知识竞赛、培训机构考核还是企业内部培训等。答题功能&#xff0c;规定答题的具体规则&#xff0c;包括题目类型&#xff08;单选、多选、…...

外观模式:简化复杂系统接口的设计模式

外观模式&#xff1a;简化复杂系统接口的设计模式 一、模式核心&#xff1a;为复杂子系统提供统一简单接口 当一个系统由多个复杂子系统组成时&#xff08;如电商系统中的支付、物流、库存模块&#xff09;&#xff0c;客户端直接调用子系统会导致依赖关系复杂、代码难以维护…...

vue3项目中eslint.config.ts配置rules

vue3项目中eslint.config.ts配置rules 1. 使用npm create vuelatest创建vue项目 默认的eslint.config.ts如下 import { globalIgnores } from eslint/config import { defineConfigWithVueTs, vueTsConfigs } from vue/eslint-config-typescript import pluginVue from esli…...

uniapp-商城-36-shop 购物车 选好了 进行订单确认2 支付方式颜色变化和颜色滤镜filter

颜色滤镜&#xff0c;在好多网页都这样使用&#xff0c;滤掉彩色&#xff0c;显示黑白&#xff0c;这在一些关键的日子中都这样使用。 1、依然回到订单确认页面 看到支付的颜色了嘛&#xff1f; <view class"payType"><view class"box" :class&q…...

Vue3 上传后的文件智能预览(实战体会)

目录 前言1. Demo12. Demo2 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器&#xff0c;无代码爬取&#xff0c;就来&#xff1a;bright.cn 此处的基本知识涉及较少&#xff0c;主要以Demo的形式供大…...

铃木一郎女儿是奥运会选手吗·棒球1号位

铃木一朗&#xff08;Ichiro Suzuki&#xff09; 铃木一朗职业生涯时间线 1973年出生于日本爱知县名古屋市。1992年以选秀第四顺位加入日本职棒&#xff08;NPB&#xff09;欧力士蓝浪队&#xff0c;开启职业棒球生涯。 1994-2000年 连续7年获得NPB太平洋联盟打击王&#xff…...

PyTorch与CUDA的关系

文章目录 前言一、如何查看PyTorch和torchvision的版本1.1 查看PyTorch版本1.2 查看torchvision版本二、如何确认PyTorch和torchvision是否支持CUDA加速2.1 检查PyTorch是否支持CUDA2.2 查看当前可用的GPU设备2.3 检查torchvision是否支持CUDA三、CUDA版本的秘密:为什么PyTorc…...

CCE13.【C++ Cont】练习题组13 静态链表专题

目录 1.B3630 排队顺序 题目 分析 代码 提交结果 2.B3631 单向链表 题目 分析 前置知识:map数组加快访问速度(简单的哈希表优化) 使用map数组的重要提醒 代码 提交结果 3.★P1160 队列安排 题目 分析 方法1:带头不循环双向链表的设计 方法2:带头循环的双向链表…...

【Mybatis】MyBatisPlus的saveBatch真的是批量插入吗?深度解析与性能优化

前言 在使用MyBatis-Plus进行批量数据插入时&#xff0c;许多开发者会发现&#xff1a;即使调用saveBatch方法&#xff0c;数据库仍会产生大量INSERT语句。本文将深入源码揭示背后的真相&#xff0c;并提供3种性能优化方案&#xff0c;让你的批量插入速度提升10倍&#xff01;…...

内联函数(c++)

预处理&#xff1a;优点&#xff1a;内嵌到目标代码&#xff0c;减少函数的调用。 缺点&#xff1a;在预处理阶段完成替换&#xff0c;避免了语义上的差错。 egg&#xff1a; #define SQR(X) ((X)*(X)) 函数&#xff1a;优点&#xff1a;完成了某一类操作的抽象&#xff0c;…...

R7周:糖尿病预测模型优化探索

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 一、数据预处理 1.设置GPU import torch.nn.functional as F import torch.nn as nn import torch, torchvisiondevice torch.device("cuda"…...

线程怎么创建?Java 四种方式一网打尽

&#x1f680; Java 中线程的 4 种创建方式详解 创建方式实现方式是否推荐场景说明1. 继承 Thread 类class MyThread extends Thread❌ 不推荐简单学习、单线程场景2. 实现 Runnable 接口class MyRunnable implements Runnable✅ 推荐更适合多线程共享资源3. 实现 Callable 接…...

前端如何连接tcp 服务,接收数据

在传统的浏览器前端环境中&#xff0c;由于浏览器的同源策略和安全限制&#xff0c;无法直接建立 TCP 连接。不过&#xff0c;可以通过 WebSocket 或者使用 WebRTC 来间接实现与 TCP 服务的通信&#xff0c;另外在 Node.js 环境中可以直接使用 net 模块建立 TCP 连接。下面分别…...

STM32之DHT11温湿度传感器---附代码

DHT11简介 DHT11的供电电压为 3&#xff0d;5.5V。 传感器上电后&#xff0c;要等待 1s 以越过不稳定状态在此期间无需发送任何指令。 电源引脚&#xff08;VDD&#xff0c;GND&#xff09;之间可增加一个100nF 的电容&#xff0c;用以去耦滤波。 DATA 用于微处理器与DHT11之间…...

工业相机——镜头篇【机器视觉,图像采集系统,成像原理,光学系统,成像光路,镜头光圈,镜头景深,远心镜头,分辨率,MTF曲线,焦距计算 ,子午弧矢】

文章目录 1 机器视觉&#xff0c;图像采集系统2 相机镜头&#xff0c;属于一种光学系统3 常规镜头 成像光路4 镜头光圈5 镜头的景深6 远心镜头 及 成像原理7 远心镜头种类 及 应用场景8 镜头分辨率10 镜头的对比度11 镜头的MTF曲线12 镜头的焦距 计算13 子午弧矢 图解 反差 工业…...

如何在Spring Boot中禁用Actuator端点安全性

在 Spring Boot 应用中&#xff0c;Spring Boot Actuator 提供了一系列用于监控和管理应用的端点&#xff08;如 /actuator/health、/actuator/metrics&#xff09;&#xff0c;这些端点默认可能受到 Spring Security 的保护&#xff0c;要求身份验证或授权。然而&#xff0c;在…...

第48讲:空间大数据与智慧农业——时空大数据分析与农业物联网的融合实践

目录 🧠 一、什么是空间大数据? 📡 二、农业物联网:数据采集的神经末梢 🔁 三、融合应用:空间大数据 + 农业IoT = 决策大脑 1. 精准灌溉管理 2. 时空病虫害预警 3. 农业碳监测与生态评估 💡 四、技术实践案例:农田干旱预警系统 📌 场景设定: 🛠 数据…...

openwrt查询网关的命令

方法一&#xff1a;route -n 方法二&#xff1a;ip route show...

华为OD机试真题——查找接口成功率最优时间段(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C、GO六种语言的最佳实现方式&#xff1b; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析&#xff1b; 本文收录于专栏&#xff1a;《2025华为OD真题目录…...

SiamMask原理详解:从SiamFC到SiamRPN++,再到多任务分支设计

SiamMask原理详解&#xff1a;从SiamFC到SiamRPN&#xff0c;再到多任务分支设计 一、引言二、SiamFC&#xff1a;目标跟踪的奠基者1. SiamFC的结构2. SiamFC的局限性 三、SiamRPN&#xff1a;引入Anchor机制的改进1. SiamRPN的创新2. SiamRPN的进一步优化 四、SiamMask&#x…...