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

news2024/11/26 20:34:10

目录

  • 一、序言
  • 二、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:value2

Body

客户端可以用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 {

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/websocket") // WebSocket握手端口
			.addInterceptors(new HttpSessionHandshakeInterceptor())
			.setAllowedOriginPatterns("*") // 设置跨域
			.withSockJS(); // 开启SockJS回退机制
	}

	@Override
	public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
		// 这里我们设置入站消息最大为8K
		registry.setMessageSizeLimit(8 * 1024);
	}

	@Override
	public 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等。

在这里插入图片描述

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

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

相关文章

(11)LCD1602液晶显示屏

LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是一种字符型液晶显示模块&#xff0c;可以显示ASCII码的标准字符和其它的一些内置特殊字符&#xff0c;还可以有8个自定义字符&#xff0c;自带芯片扫描 显示容量&#xff1a;162个字符&#xff0c;每个字符…

【C++】STL六大组件简介

STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 1.STL的版本介绍 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本&#xff…

Unity里面CG和HLSL在写法上的一些区别

大家好&#xff0c;我是阿赵。这里继续讲URP相关的内容。 这次想讲的是CG和HLSL在写法上的一些区别。 一、为什么开始用HLSL 首先&#xff0c;基本上大家都知道的事情再说一遍。 三种Shader编程语言&#xff1a; 1、基于OpenGL的OpenGL Shading Language&#xff0c;缩写GLSL…

接口测试中postman环境和用例集

postman的环境使用 postman里有环境的设置&#xff0c;就是我们常说的用变量代替一个固定的值&#xff0c;这样做的好处是可以切换不同的域名、不同的环境变量&#xff0c;不同的线上线下账户等等场景。下面就看下怎么用吧。 创建一个Environment postman有一个envrionment&am…

Java是如何实现双亲委托机制的

Java 是一种面向对象的编程语言&#xff0c;它有一套独特的类加载机制。其中&#xff0c;双亲委托加载机制是 Java 类加载机制中的一个重要概念。本文将介绍 Java 的双亲委托加载机制是如何实现的&#xff0c;并解释其作用和优点。 Java 类加载机制 在 Java 中&#xff0c;类的…

瀑布流组件陷入商品重复怪圈?我是如何用心一解的!

目录 背景 瀑布流组件 什么是瀑布流组件 如何实现一个瀑布流组件 商品重复的原因 解决方案 方法一&#xff08;复杂&#xff0c;不推荐&#xff09;&#xff1a;标记位大法 方法二&#xff08;优雅&#xff0c;推荐&#xff09;&#xff1a;Promise 队列 大法 总结 背…

解决新思路#报错:ping: www.baidu.com: 未知的名称或服务--照着步骤来还是ping不通的可能原因

最近由ubantu转为centos7,配置hadoop&#xff0c;配置静态ip的过程中一直ping不通。之前配置ubantu的也是&#xff0c;终于这次在重启虚拟机和主机后发现了原因。 中途尝试过: 1.三次以上命令行反复操作 2.图形界面设置 3.看是否主机的网络适配器的网关与设置的IP地址产生冲突…

JavaScript实现计算100之间能被5整除的数的代码

以下为实现计算100之间能被5整除的数的程序代码和运行截图 目录 前言 一、计算100之间能被5整除的数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题…

2023最新100道渗透测试面试题(附答案)

眨眼间2023年快过去一半了&#xff0c;不知道大家有没有找到心仪的工作呀&#xff0c;今天我给大家整理了100道渗透测试面试题给大家&#xff0c;需要答案的话可以在评论区给我留言哦~ 第一套渗透面试题 什么是渗透测试&#xff1f;它的目的是什么&#xff1f; 渗透测试的五个…

DirectX12 简单入门(一)

在很久以前写过关于DirectX9的一些应用&#xff0c;直到现在DirectX12已经普及了。写完几个DirectX12测试代码之后&#xff0c;写一篇DirectX12简单入门介绍一下基本概念&#xff0c;以及环境搭建和编程过程。 编程环境 与DirectX9不同&#xff0c;在DirectX12开发中微软将需…

『MySQL 实战 45 讲』“order by” 是怎么工作的

“order by” 是怎么工作的 首先创建一个表 CREATE TABLE t ( id int(11) NOT NULL, city varchar(16) NOT NULL, name varchar(16) NOT NULL, age int(11) NOT NULL, addr varchar(128) DEFAULT NULL, PRIMARY KEY (id), KEY city (city) ) ENGINEInnoDB;全字段排序 在 cit…

自己搭建go web 框架

思想base部分day1:封装gee封装context上下文封装tree路由树分组封装group与中间件封装文件解析封装封装错误处理 思想 web框架服务主要围绕着请求与响应来展开的 搭建一个web框架的核心思想 1 便捷添加响应路径与响应函数(base) 2 能够接收多种数据类型传入(上下文context) 3 构…

【Linux】Linux入门学习之常用命令五

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

支付系统设计五:对账系统设计01-总览

文章目录 前言一、对账系统构建二、执行流程三、获取支付渠道数据1.接口形式1.1 后台配置1.2 脚本编写1.2.1 模板1.2.2 解析脚本 2.FTP形式2.1 后台配置2.2 脚本编写2.2.1 模板2.2.2 解析脚本 四、获取支付平台数据五、数据比对1. 比对模型2. 比对器 总结 前言 从《支付系统设…

AE基础教程

一&#xff1a;粒子插件。 AEPR插件-Trapcode Suite V18.1.0 中文版 二&#xff1a;跟随手指特效。 1&#xff1a;空对象位置关键帧跟随手指。 2&#xff1a;发射粒子位置&#xff0c;按住Alt键&#xff0c;连接到空对象位置处。。 三&#xff1a;CtrI导入素材快捷键。 四&a…

Elasticsearch基础学习-常用查询和基本的JavaAPI操作ES

关于ES数据库的和核心倒排索引的介绍 一、Elasticsearch概述简介关于全文检索引擎关系型数据库的全文检索功能缺点全文检索的应用场景Elasticsearch 应用案例 二、Elasticsearch学习准备安装下载关于es检索的核心-倒排索引正向索引&#xff08;forward index&#xff09;倒排索…

辅助驾驶功能开发-功能规范篇(16)-2-领航辅助系统NAP-自动变道-1

书接上回 2.3.4.自动变道 当车辆处于导航引导模式NOA功能时(即车辆横向控制功能激活),且车速大于40km/h,驾驶员按下转向灯拨杆或系统判断当前有变道需要时,自动变道系统通过对车道线、自车道前方目标距离、邻近车道前后方目标距离等环境条件进行判断,在转向灯亮起3s后控…

看到这个数据库设计,我终于明白了我和其他软测人的差距

测试人员为什么要懂数据库设计&#xff1f; 更精准的掌握业务&#xff0c;针对接口测试、Web 测试&#xff0c;都是依照项目/产品需求进行用例设计&#xff0c;如果掌握数据库设计知识&#xff0c;能直接面对开发的数据表&#xff0c;更好、更精准的理解业务逻辑&#xff1b;有…

【滑动窗口】滑窗模板,在小小的算法题里滑呀滑呀滑

首先大家先喊出我们的口号&#xff1a;跟着模板搞&#xff0c;滑窗没烦恼&#xff01; 一.什么是滑动窗口&#xff1f; 滑动窗口算法是双指针算法的一种特定化的算法模型&#xff0c;常用于在特定的条件下求最大或者最小的字符串&#xff0c;特定的数组&#xff0c;以及字符序列…

JAVA 可用的高性能docker镜像及如何使用?

目前docker hub上下载量很大的java、openjdk镜像都已经被弃用,不再维护,目前可用的java docker镜像有哪一些呢?哪一些镜像是主流的? 本文带有领略java可用的镜像资源、如何使用它们,如何构建springboot镜像? 1. 可用的java镜像 1.1. amazoncorretto 1.1.1. 什么是Corr…