当前位置: 首页 > 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中的容器适配器与此类似,是一个封装了序列容器的类模板,它在一般序列容器的基础上提供了一些不同的功能和接口。之所…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...