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

WebSocket的那些事(3-STOMP实操篇)

目录

  • 一、序言
  • 二、STOMP详解
    • 1、STOMP简单介绍
    • 2、STOMP协议内容
    • 3、使用STOMP的好处
  • 三、代码示例
    • 1、Maven依赖
    • 2、开启WebSocket消息代理
    • 3、控制器
    • 4、前端页面greeting.html
  • 四、测试
    • 1、连接服务端
    • 2、发送消息
  • 五、STOMP消息传播流程
  • 六、结语

一、序言

上节中我们在 WebSocket的那些事(2-实操篇)中简单介绍了Spring中对于WebSocket的封装,并实现一个简单的服务端,这节我们将会结合STOMP子协议实现WebSocket通信。


二、STOMP详解

1、STOMP简单介绍

WebSocket协议定义了两种消息类型(文本类型和二进制类型),但是消息内容却是未定义的,下面我们介绍一下STOMP协议。

STOMP (Simple Text Oriented Messaging Protocol) 起源于脚本语言,比如Ruby、Python和Perl,用于连接企业消息代理,它可以用于任何可靠的双向网络协议中,如TCP和WebSocket。尽管STOMP是一个面向文本的协议,但消息负载可以是文本或者二进制。

STOMP基于WebSocket在客户端和服务端之间定义了一种机制,协商通过子协议(更高级的消息协议)来定义可以发送何种消息,每条消息的内容是什么,等等。

2、STOMP协议内容

STOMP是一个基于帧的协议,帧的结构如下:

COMMAND
header1:value1
header2:value2Body

客户端可以用SEND或者SUBSCRIBE命令去发送和订阅消息,destination头部用来描述消息发送到哪里以及谁应该接收消息,下面的消息结构是客户端订阅股票行情的例子,如下:

SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*

下面的消息结构是客户端发送交易请求的例子,如下:

SEND
destination:/queue/trade
content-type:application/json
content-length:44{"action":"BUY","ticker":"MMM","shares",44}

STOMP服务端可以使用MESSAGE 命令广播消息给所有的订阅者,下面的例子为广播股票行情消息给所有消息订阅者。

MESSAGE
message-id:nxahklf6-1
subscription:sub-1
destination:/topic/price.stock.MMM{"ticker":"MMM","price":129.45}

3、使用STOMP的好处

在Spring中使用STOMP与原生WebSockets相比提供了更加丰富的编程模型,下面是使用STOMP的优点:

  • 不需要发明自定义消息协议和消息格式。
  • 在Spring中,STOMP客户端,包括Java客户端都可用。
  • 可使用其它消息代理管理消息订阅和广播,如RabbitMQActiveMQ等支持STOMP协议的中间件。
  • 应用逻辑处理入口可以像Spring MVC一样统一在@Controller实例内,同时消息可以基于STOMP头部消息进行路由,而不是直接用WebSocketHandler处理原生WebSocket消息。
  • 可以基于STOMP目的地和消息类型使用Spring Security对消息进行安全传输。

三、代码示例

1、Maven依赖

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

备注:这里我就简单用Thymeleaf作模板引擎,为了方便调试在application.yml中将spring.thymeleaf.cache设为false来禁用模板缓存。

2、开启WebSocket消息代理

@Configuration
@EnableWebSocketMessageBroker
public class WebsocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/websocket") // WebSocket握手端口.addInterceptors(new HttpSessionHandshakeInterceptor()).setAllowedOriginPatterns("*") // 设置跨域.withSockJS(); // 开启SockJS回退机制}@Overridepublic void configureWebSocketTransport(WebSocketTransportRegistration registry) {// 这里我们设置入站消息最大为8Kregistry.setMessageSizeLimit(8 * 1024);}@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.setApplicationDestinationPrefixes("/app") // 发送到服务端目的地前缀.enableSimpleBroker("/topic");// 开启简单消息代理,指定消息订阅前缀}}

备注:

  1. 关于SockJS的介绍请参考上篇文章 WebSocket的那些事(1-概念篇)最后部分。
  2. setApplicationDestinationPrefixes的意思是以目的地以/app开头的消息将会被路由到Controller实例中的方法进行处理。
  3. enableSimpleBroker将会启用一个内置的内存消息代理,用于订阅、广播和路由消息目的地到以/topic开头代理中。

3、控制器

@Slf4j
@Controller
public class GreetingController {@GetMapping("/page/greeting")public ModelAndView turnToGreetingPage() {return new ModelAndView("/greeting");}@MessageMapping("/greeting")public String sayGreeting(String name) {log.info("Message received: {}", name);return "Hello, " + name;}
}

4、前端页面greeting.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>greeting</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 src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script><style>#mainWrapper {width: 600px;margin: auto;}</style>
</head>
<body>
<div id="mainWrapper"><div id="msgWrapper"><p style="vertical-align: top">发送的消息:</p><textarea id="msgSent" style="width: 400px;height: 100px"></textarea><p style="vertical-align: top">收到的消息:</p><textarea id="msgReceived" style="width: 400px;height: 100px"></textarea></div><div style="margin-top: 5px;"><button onclick="connect()">连接</button><button onclick="sendMessage()">发送消息</button><button onclick="disconnect()">断开连接</button></div>
</div>
<script type="text/javascript">$(() => {$('#msgSent').val('');$("#msgReceived").val('');});let stompClient = null;// 连接服务器const connect = () => {const ws = new SockJS('http://localhost:8080/websocket');stompClient = Stomp.over(ws);stompClient.connect({}, () => {$("#msgReceived").val('Connected to websocket server!');stompClient.subscribe('/topic/greeting', function (message) {console.log(`Received message: ${message.body}`);$("#msgReceived").val(message.body);});});}// 断连const disconnect = () => {stompClient.disconnect(() => {$("#msgReceived").val('Disconnected from WebSocket server');});}// 发送消息,这里直接发的是文本const sendMessage = () => {stompClient.send('/app/greeting', {}, $('#msgSent').val());}
</script>
</body>
</html>

备注:

  1. greeting.htmlThymeleaf模板页面,文件直接放在类路径templates目录下即可.
  2. 这里我们选用了SockJS去连接服务端,为什么使用SockJS请参考 WebSocket的那些事(1-概念篇),同时注意协议名称是http而不是ws

四、测试

浏览器输入http://localhost:8080/page/greeting即可跳转到我们的页面,如下:
在这里插入图片描述

1、连接服务端

点击连接按钮后,我们发现已经与服务端成功建立连接。我们可以观察到客户端发送了两条命令,一条是Connect建立连接命令,另一条是连接建立成功后的SUBSCRIBE命令,用来订阅目的地以/topic/greeting开头的消息。

在这里插入图片描述

2、发送消息

点击发送按钮发送消息,可以看到服务端也返回了Hello, Nick的回复消息。
在这里插入图片描述

备注:我们可以看到客户端发送了一条SEND命令,目的地为/app/greeting,内容长度为4个字节。同时服务端回复了一条MESSAGE命令,这条命令的意思是广播消息给所有订阅/topic/greeting的客户端。


五、STOMP消息传播流程

一旦STOMP端点暴露,Spring应用对于连接的客户端就变成了STOMP代理,下面我们简单介绍一下服务端消息传播流程。
在这里插入图片描述
上面的图展示了三种消息渠道:

  • clientInboundChannel: 客户端消息入站通道,用来传递WebSocket客户端接收的消息。
  • clientOutboundChannel: 客户端消息出站通道,用来发送服务端消息给WebSocket客户端。
  • brokerChannel: 消息代理通道,用来在服务层传递消息给消息代理。

目的地以/app/a开头的消息会通过clientInboundChannel路由到MessageHandler消息处理器进行处理,然后再将消息通过brokerChannel发送到SimpleBroker中。

目的地以/topic/a开头的消息会通过clientInboundChannel直接路由到SimpleBroker中进行处理。

上图中的SimpleBroker实际上是Spring基于内存的内置消息代理,实际上Spring也支持集成其它支持STOMP协议的MQ,如ActiveMQ、RabbitMQ等,集成外部消息代理的消息传播流程图如下:

在这里插入图片描述
从上图中,我们可以看到StompBrokerRelay会和外部消息代理进行通信,通过enableStompBrokerRelay即可以集成外部消息代理,关于集成外部消息代理的例子和细节我们在后面的章节在讨论。


六、结语

这节我们简单介绍了STOMP协议、Spring中对WebSocketSTOMP支持以及具体的代码集成示例。

下节我们将会具体介绍@Controller控制器中相关消息注解的使用示例和细节,如@MessageMapping@SubscribeMapping@MessageExceptionHandler@SendTo@SendToUser等。

在这里插入图片描述

相关文章:

WebSocket的那些事(3-STOMP实操篇)

目录 一、序言二、STOMP详解1、STOMP简单介绍2、STOMP协议内容3、使用STOMP的好处 三、代码示例1、Maven依赖2、开启WebSocket消息代理3、控制器4、前端页面greeting.html 四、测试1、连接服务端2、发送消息 五、STOMP消息传播流程六、结语 一、序言 上节中我们在 WebSocket的…...

《花雕学AI》WeTab+ChatGPT:让浏览器变成你的智能助手

引言&#xff1a; 浏览器是我们日常使用的最重要的工具之一&#xff0c;它可以帮助我们获取信息、娱乐、学习、工作等。但是&#xff0c;传统的浏览器往往不能满足我们的个性化需求&#xff0c;也不能给我们提供智能化的服务。那么&#xff0c;有没有一种浏览器可以让我们的体…...

MySQL 字段为 NULL 的5大坑,大部分人踩过

数据库字段允许空值(null)的问题&#xff0c;小伙伴你遇到过吗&#xff1f; 在验证问题之前&#xff0c;我们先建一张测试表及测试数据。 构建的测试数据&#xff0c;如下图所示&#xff1a; 有了上面的表及数据之后&#xff0c;我们就来看当列中存在 NULL 值时&#xff0c;究…...

Android SystemUI篇(二)

目录 一、简介 二、SystemUI的架构 三、SystemUI的主要组件 四、SystemUI的主要功能 五、SystemUI的自定义和定制 六、SystemUI的性能优化 一、简介 SystemUI是Android操作系统的一个关键组件&#xff0c;主要负责管理和提供用户界面的核心元素&#xff0c;如状态栏、导航…...

第六讲:“声音”写具体

爸爸又打呼了!“呼噜一呼噜一像一股巨浪腾空而起&#xff0c;以每秒八十米的速度向上冲刺&#xff0c;力图掀开天花板&#xff0c;掀翻整座住宅楼;“呼噜一一呼噜一-”&#xff0c;像一台轰鸣的坦克在穿行&#xff0c;床垫在抖动&#xff0c;吊灯在摇晃&#xff0c;墙灰在簌籁(…...

Unity 向量

向量的加减法本文就不再赘述了&#xff0c;本文侧重介绍脚本中的向量写法 一、向量的数乘 定义&#xff1a;k(x,y,z)(kx,ky,kz) 若向量长度为L&#xff0c;k取1/L&#xff0c;就恰好能让原向量长度变成1&#xff0c;变成了单位向量&#xff0c;这称为向量的标准化 。 由于长度…...

Apache Tomcat AJP协议文件读取与包含

永远也不要忘记能够笑的坚强&#xff0c;就算受伤&#xff0c;我也从不彷徨。 0x01.漏洞情况分析 Tomcat是Apache软件基金会Jakarta 项目中的一个核心项目&#xff0c;作为目前比较流行的Web应用服务器&#xff0c;深受Java爱好者的喜爱&#xff0c;并得到了部分软件开发商的…...

实验10 人工神经网络(1)

1. 实验目的 ①理解并掌握误差反向传播算法&#xff1b; ②能够使用单层和多层神经网络&#xff0c;完成多分类任务&#xff1b; ③了解常用的激活函数。 2. 实验内容 ①设计单层和多层神经网络结构&#xff0c;并使用TensorFlow建立模型&#xff0c;完成多分类任务&#xf…...

OPPO关停哲库业务,工程师造芯何去何从?

5月12日&#xff08;上周五&#xff09;&#xff0c;新浪科技从OPPO处了解到&#xff0c;OPPO将终止ZEKU业务。3000多人团队突然原地解散&#xff0c;网上唏嘘声一片&#xff01; ZEKU最初成立于2019年&#xff0c;是OPPO的全资子公司&#xff0c;欧加集团百分之百注资成立。总…...

面试被问麻了....

前几天组了一个软件测试面试的群&#xff0c;没想到效果直接拉满&#xff0c;看来大家对面试这块的需求还是挺迫切的。昨天我就看到群友们发的一些面经&#xff0c;感觉非常有参考价值&#xff0c;于是我就问他还有没有。 结果他给我整理了一份非常硬核的面筋&#xff0c;打开…...

AspNetCore中的配置文件详解

1 配置文件 程序开发中&#xff0c;有些信息是要根据环境改变的&#xff0c;比如开发环境的数据库可能是本地数据&#xff0c;而生产环境下需要连接生产数据库&#xff0c;我们需要把这些信息放到程序外面&#xff0c;在程序运行时通过读取这些外部信息实现不改变程序代码适应…...

实时更新天气微信小程序开发

1.新建一个天气weather项目 2.在app.json中创建一个路由页面 当我们点击保存的时候&#xff0c;微信小程序会自动的帮我们创建好页面 3.在weather页面上书写我们的骨架 4.此时我们的页面很怪&#xff0c;因为没有给它添加样式和值。此时我们给它一个样式。&#xff08;样式写在…...

css渐变

线性渐变 liner-gradient属性值用来设置线性渐变&#xff0c;第一个参数值是方向&#xff0c;默认是从上往下&#xff0c;往后就是渐变颜色的种类。 background-image:liner-gradient(方向&#xff0c;颜色1&#xff0c;颜色2...) .box {display: flex;width: 400px;height: …...

《斯坦福数据挖掘教程·第三版》读书笔记(英文版) Chapter 2 MapReduce and the New Software Stack

来源&#xff1a;《斯坦福数据挖掘教程第三版》对应的公开英文书和PPT Chapter 2 MapReduce and the New Software Stack Computing cluster means large collections of commodity hardware, including conventional processors (“compute nodes”) connected by Ethernet …...

HTML零基础快速入门(详细教程)

1&#xff0c;HTML代码特点 <html><head></head><body>hello world!</body> </html>HTML代码有以下特点&#xff1a; html代码是通过标签来组织的&#xff0c;而标签是由尖括号< >组织的&#xff0c;也可被叫作元素&#xff08;ele…...

Kubernetes第5天

第七章 Service详解 本章节主要介绍kubernetes的流量负载组件&#xff1a;Service和Ingress。 Service介绍 ​ 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着…...

RK3568平台开发系列讲解(调试篇)debugfs 分析手段

🚀返回专栏总目录 文章目录 一、enable debugfs二、debugfs API三、使用示例沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 上有一些典型的问题分析手段,从这些基本的分析方法入手,你可以一步步判断出问题根因。这些分析手段,可以简单地归纳为下图: 从这…...

【Spring框架全系列】SpringBoot配置日志文件

&#x1f367;&#x1f367;哈喽&#xff0c;大家好&#xff0c;我是小浪。那么上篇博客我们学习了SpringBoot配置文件的相关操作&#xff0c;本篇博客我们将学习一个新的知识点&#xff0c;SpringBoot日志文件。&#x1f5a5;&#x1f5a5; &#x1f4f2;目录 一、日志是什么…...

事务 ---MySQL的总结(六)

事务 多进程进行并改变同一个数据&#xff0c;如果没有进行版本控制&#xff0c;就会出现数据不确定的问题&#xff0c;为此引入了事务的概念。可以进行数据回滚&#xff0c;解决潜在的问题。 事务的概念 一组的DML组成&#xff0c;这一些的DML要么同时成功&#xff0c;要么同…...

22 标准模板库STL之容器适配器

概述 提到适配器,我们的第一印象是想到设计模式中的适配器模式:将一个类的接口转化为另一个类的接口,使原本不兼容而不能合作的两个类,可以一起工作。STL中的容器适配器与此类似,是一个封装了序列容器的类模板,它在一般序列容器的基础上提供了一些不同的功能和接口。之所…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案

引言 在分布式系统的事务处理中&#xff0c;如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议&#xff08;2PC&#xff09;通过准备阶段与提交阶段的协调机制&#xff0c;以同步决策模式确保事务原子性。其改进版本三阶段提交协议&#xff08;3PC&#xf…...

[特殊字符] 手撸 Redis 互斥锁那些坑

&#x1f4d6; 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作&#xff0c;想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁&#xff0c;也顺便跟 Redisson 的 RLock 机制对比了下&#xff0c;记录一波&#xff0c;别踩我踩过…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...

用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章

用 Rust 重写 Linux 内核模块实战&#xff1a;迈向安全内核的新篇章 ​​摘要&#xff1a;​​ 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言&#xff0c;受限于 C 语言本身的内存安全和并发安全问题&#xff0c;开发复杂模块极易引入难以…...

【Java多线程从青铜到王者】单例设计模式(八)

wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本&#xff0c;sleep也是可以指定时间的&#xff0c;也就是说时间一到就会解除阻塞&#xff0c;继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒)&#xff0c;wait能被notify提前唤醒&#xf…...

工厂方法模式和抽象工厂方法模式的battle

1.案例直接上手 在这个案例里面&#xff0c;我们会实现这个普通的工厂方法&#xff0c;并且对比这个普通工厂方法和我们直接创建对象的差别在哪里&#xff0c;为什么需要一个工厂&#xff1a; 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类&#xff1a; 两个发…...

安宝特方案丨从依赖经验到数据驱动:AR套件重构特种装备装配与质检全流程

在高压电气装备、军工装备、石油测井仪器装备、计算存储服务器和机柜、核磁医疗装备、大型发动机组等特种装备生产型企业&#xff0c;其产品具有“小批量、多品种、人工装配、价值高”的特点。 生产管理中存在传统SOP文件内容缺失、SOP更新不及、装配严重依赖个人经验、产品装…...

八、【ESP32开发全栈指南:UDP客户端】

1. 环境准备 安装ESP-IDF v4.4 (官方指南)确保Python 3.7 和Git已安装 2. 创建项目 idf.py create-project udp_client cd udp_client3. 完整优化代码 (main/main.c) #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h&…...

RK3568项目(七)--uboot系统之外设与PMIC详解

目录 一、引言 二、按键 ------>2.1、按键种类 ------------>2.1.1、RESET ------------>2.1.2、UPDATE ------------>2.1.3、PWRON 部分 ------------>2.1.4、RK809 PMIC ------------>2.1.5、ADC按键 ------------>2.1.6、ADC按键驱动 ------…...