Websocket在Java中的实践——整合Rabbitmq和STOMP

大纲

  • Rabbitmq
    • 开启STOMP支持
  • 服务端
    • 依赖
    • 参数
    • 参数映射类
    • 配置类
    • 逻辑处理类
  • 测试
    • 测试页面
    • Controller
    • 测试案例

在《Websocket在Java中的实践——STOMP通信的最小Demo》一文中,我们使用enableSimpleBroker启用一个内置的内存级消息代理。本文我们将使用Rabbitmq作为消息代理,这样我们的服务就可以变成分布式部署。

Rabbitmq

开启STOMP支持

在Rabbitmq所在的机器上执行下面的命令:

sudo -H -u rabbitmq bash -c "/usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_stomp"

在这里插入图片描述
然后启动Rabbitmq

sudo service rabbitmq-server start

服务端

依赖

spring-boot-starter-websocket用于Websocket服务。
spring-boot-starter-amqp和spring-rabbit-stream都是用于Rabbitmq操作。
reactor-netty用于Broker。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit-stream</artifactId>
</dependency>
<dependency><groupId>io.projectreactor.netty</groupId><artifactId>reactor-netty</artifactId><version>1.1.20</version>
</dependency>

参数

src/main/resources/application.properties
需要注意的是,rabbitmq_stomp启动后会开启61613端口。

spring.rabbitmq.host=172.30.254.255
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=fangliang
spring.rabbitmq.stomp.port=61613

在这里插入图片描述
还有一点需要注意,很多文章上说使用guest用户登录。但是guest用户只能在Rabbitmq所在的机器上使用,如果跨机器使用会报下列错误。而且这和是否设置guest为全域无关。所以我们使用admin账户。

Received ERROR {message=[Bad CONNECT], content-type=[text/plain], version=[1.0,1.1,1.2], content-length=[26]} session=system text/plain payload=non-loopback access denied

spring.rabbitmq.stomp.port是一个自定义参数,它只是供Broker连接Rabbitmq使用。
spring.rabbitmq.port在当前本文例子中没有使用。

参数映射类

这个类主要是映射上述参数,方便后续使用。
src/main/java/com/nyctlc/stomprbmq/component/RabbitMQProperties.java

package com.nyctlc.stomprbmq.component;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class RabbitMQProperties {@Value("${spring.rabbitmq.password}")private String rabbitmqPassword;public String getRabbitmqPassword() {return rabbitmqPassword;}@Value("${spring.rabbitmq.username}")private String rabbitmqUsername;public String getRabbitmqUsername() {return rabbitmqUsername;}@Value("${spring.rabbitmq.host}")private String rabbitmqHost;public String getRabbitmqHost() {return rabbitmqHost;}@Value("${spring.rabbitmq.port}")private String rabbitmqPort;public String getRabbitmqPort() {return rabbitmqPort;}@Value("${spring.rabbitmq.stomp.port}")private String rabbitmqStompPort;public String getRabbitmqStompPort() {return rabbitmqStompPort;}
}

配置类

/handshake是STOMP和Websocket建立握手的接口。
enableStompBrokerRelay(“/topic”)会订阅Rabbitmq默认的交换器amq.topic的绑定关系中定义的队列。(所以我们看到很多文章订阅的前缀使用的是“topic”,而不用其他字段,这是有渊源的)
在这里插入图片描述
在这里插入图片描述

setRelayPort方法传递的是Rabbitmq的STOMP端口,即61613。
setClientLogin、setClientPasscode、setSystemLogin和setSystemPasscode都要设置为admin及其密码,否则会报错。

src/main/java/com/nyctlc/stomprbmq/config/WebSocketConfig.java

package com.nyctlc.stomprbmq.config;import org.springframework.beans.factory.annotation.Autowired;
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;import com.nyctlc.stomprbmq.component.RabbitMQProperties;@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Autowiredprivate RabbitMQProperties rabbitMQProperties;@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/handshake");}@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.setApplicationDestinationPrefixes("/send");registry.enableStompBrokerRelay("/topic").setRelayHost(rabbitMQProperties.getRabbitmqHost()).setRelayPort(Integer.parseInt(rabbitMQProperties.getRabbitmqStompPort())).setClientLogin(rabbitMQProperties.getRabbitmqUsername()).setClientPasscode(rabbitMQProperties.getRabbitmqPassword()).setSystemLogin(rabbitMQProperties.getRabbitmqUsername()).setSystemPasscode(rabbitMQProperties.getRabbitmqPassword());}
}

逻辑处理类

这个类的handle方法会接受/send/msg-from-user端点发来的消息,然后转发给Rabbitmq的amp.topic交换器下msg-to-user路由键对应的队列。上述代码创建的Broker会持续监听这个队列,如果收到消息,则发送给客户端。

src/main/java/com/nyctlc/stomprbmq/controller/WebSocketController.java

package com.nyctlc.stomprbmq.controller;import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;@Controller
public class WebSocketController {@MessageMapping("/msg-from-user")@SendTo("/topic/msg-to-user")public String handle(String msg) {System.out.println("Received message: " + msg);return msg;}
}

测试

测试页面

src/main/resources/static/index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>STOMP over WebSocket Example with StompJs.Client</title><script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs"></script>
</head>
<body><h2>STOMP over WebSocket Example with StompJs.Client</h2><button id="connectButton">Connect</button><form id="messageForm"><input type="text" id="messageInput" placeholder="Type a message..."/><button type="submit">Send</button></form><div id="messages"></div><script>var client = null;function connect() {client = new StompJs.Client({brokerURL: 'ws://localhost:8080/handshake', // WebSocket服务端点connectHeaders: {},debug: function (str) {console.log(str);},reconnectDelay: 5000,heartbeatIncoming: 4000,heartbeatOutgoing: 4000,});client.onConnect = function(frame) {console.log('Connected: ' + frame);client.subscribe('/topic/msg-to-user', function(message) { // 订阅端点showMessageOutput(JSON.parse(message.body).content);});};client.onStompError = function(frame) {console.error('Broker reported error: ' + frame.headers['message']);console.error('Additional details: ' + frame.body);};client.activate();}function sendMessage(event) {event.preventDefault(); // 阻止表单默认提交行为var messageContent = document.getElementById('messageInput').value.trim();if(messageContent && client && client.connected) {var chatMessage = { content: messageContent };client.publish({destination: "/send/msg-from-user", body: JSON.stringify(chatMessage)}); // 发送端点document.getElementById('messageInput').value = '';}}function showMessageOutput(message) {var messagesDiv = document.getElementById('messages');var messageElement = document.createElement('div');messageElement.appendChild(document.createTextNode(message));messagesDiv.appendChild(messageElement);}document.getElementById('messageForm').addEventListener('submit', sendMessage);document.getElementById('connectButton').addEventListener('click', connect);</script>
</body>
</html>

Controller

这个Controller主要是为了让上述HTML可以通过URL访问。
src/main/java/com/nyctlc/stomprbmq/controller/FileController.java

package com.nyctlc.stomprbmq.controller;import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;@Controller
public class FileController {@GetMapping("/")public String index() {return "index"; // 返回index.html}@RequestMapping(value = "/favicon.ico")@ResponseStatus(value = HttpStatus.NO_CONTENT)public void favicon() {// No operation. Just to avoid 404 error for favicon.ico}
}

测试案例

在这里插入图片描述
在这里插入图片描述
我们在管理后台直接给这个队列发送消息,前端页面也会收到。比如我们发送{“content”:“message from management”}
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

【Unity2D 2022:Particle System】添加拾取粒子特效

一、创建粒子特效游戏物体 二、修改粒子系统属性 1. 基础属性 &#xff08;1&#xff09;修改发射粒子持续时间&#xff08;Duration&#xff09;为3s &#xff08;2&#xff09;取消勾选循环&#xff08;Looping&#xff09; &#xff08;2&#xff09;修改粒子存在时间&…

SQL性能优化策略

发现问题 通过业务监控发现慢SQL或接口响应延迟。利用性能分析工具定位问题。 定位SQL语句 使用监控工具确定影响性能的SQL语句和表。 SQL查询变慢原因 索引失效&#xff1a;查询未使用索引或索引效率低。多表连接&#xff1a;JOIN操作导致性能下降。查询字段过多&#xf…

Monitor结构解读之EntryQ和WaitSet的区别

EntryQ&#xff08;或_EntryList&#xff09;和WaitSet&#xff08;或_WaitSet&#xff09;在Java的monitor机制中扮演着不同的角色&#xff0c;它们之间的主要区别体现在以下几个方面&#xff1a; 1. 等待原因和机制 EntryQ&#xff08;或_EntryList&#xff09;&#xff1a…

面试常考题---128陷阱(详细)

1.问题引入 分别引入了int和Integer变量&#xff0c;并进行比较 int b 128; int b1 128;Integer d 127; Integer d1 127;Integer e 128; Integer e1 128;System.out.println(bb1); System.out.println(dd1); System.out.println(ee1); System.out.println(e.equals(e1)…

刷题(day01)

1、leetcode485.最大连续1的个数 给定一个二进制数组 nums &#xff0c; 计算其中最大连续 1 的个数。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,0,1,1,1] 输出&#xff1a;3 解释&#xff1a;开头的两位和最后的三位都是连续 1 &#xff0c;所以最大连续 1 的个数是 3.…

Nginx 高效加速策略:动静分离与缓存详解

在现代Web开发中&#xff0c;网站性能是衡量用户体验的关键指标之一。Nginx&#xff0c;以其出色的性能和灵活性&#xff0c;成为众多网站架构中不可或缺的一部分。本文将深度解析如何利用Nginx实现动静分离与缓存&#xff0c;从而大幅提升网站加载速度和响应效率。 理解动静分…

昇思第18天打卡|ShuffleNet图像分类

ShuffleNet网络介绍 ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型&#xff0c;和MobileNet, SqueezeNet等一样主要应用在移动端&#xff0c;所以模型的设计目标就是利用有限的计算资源来达到最好的模型精度。ShuffleNetV1的设计核心是引入了两种操作&#xff1a;Pointw…

张大哥笔记:你一旦开窍,就会发现遍地都是钱

大家有没有发现&#xff0c;穷人总是追逐眼前的利益&#xff0c;总是在追着钱跑&#xff0c;却总是赚不到钱。而富人有着长远的见识&#xff0c;追着问题跑&#xff0c;最后却赚的盆满钵满。 我们听过这样一句话&#xff0c;钱不是赚来的&#xff0c;而是帮助别人解决问题后给你…

【计算机】同步/异步

同步/异步 在计算机科学和编程中&#xff0c;“同步”&#xff08;Synchronization&#xff09;是一种机制&#xff0c;用于协调不同进程或线程之间的操作&#xff0c;以避免竞态条件&#xff08;race conditions&#xff09;、死锁&#xff08;deadlocks&#xff09;和其他并…

Qt/C++编写地图应用/离线地图下载/路径规划/轨迹回放/海量点/坐标转换

一、前言说明 这个地图组件写了很多年了&#xff0c;最初设计的比较粗糙&#xff0c;最开始只是为了满足项目需要&#xff0c;并没有考虑太多拓展性&#xff0c;比如最初都是按照百度地图写死在代码中&#xff0c;经过这几年大量的现场实际应用&#xff0c;以及大量的用户提出…

Django 新增数据 save()方法

1&#xff0c;添加模型 Test/app11/models.py from django.db import modelsclass Book(models.Model):title models.CharField(max_length100)author models.CharField(max_length100)publication_date models.DateField()price models.DecimalField(max_digits5, decim…

BFC 是什么?

BFC 是块级格式化上下文&#xff08;Block Formatting Context&#xff09;的缩写&#xff0c;是 CSS 中一个重要的概念&#xff0c;用于控制块级盒子的布局及浮动元素的交互。BFC 是一个独立的渲染区域&#xff0c;内部的块级盒子会按照特定的规则进行布局&#xff0c;不会影响…

软件工程(上)

目录 软件过程模型&#xff08;软件开发模型&#xff09; 瀑布模型 原型模型 V模型 构件组装模型 螺旋模型&#xff08;原型瀑布&#xff09; 基于构件的软件工程&#xff08;CBSE&#xff09; 快速应用开发模型&#xff08;RAD&#xff09; 统一过程&#xff08;UP&a…

Linux学习看这一篇就够了,超超超牛的Linux基础入门

引言 小伙伴们&#xff0c;不管是学习c还是学习其他语言在我们学的路上都绕不过操作系统&#xff0c;而且&#xff0c;老生常谈的Linux更是每个计算机人的必修&#xff0c;那么我们对Linux的了解可能只是从别人那听到的简单的这个系统很牛&#xff0c;巴拉巴拉的&#xff0c;但…

大模型日报 2024-07-08

大模型日报 2024-07-08 大模型资讯 Anthropic CEO&#xff1a;大模型训练成本暴涨&#xff0c;2027年将达1000亿美元&#xff01; Anthropic首席执行官表示&#xff0c;当前AI模型训练成本是10亿美元&#xff0c;未来三年&#xff0c;这个数字可能会上升到100亿美元甚至1000亿美…

GitLab管理员常用配置及设置汇总

​ 之前在 虚拟机Ubuntu 22.04上搭建GitLab操作步骤 上介绍了在Ubuntu 22.04上如何搭建社区版的GitLab&#xff0c;这里整理下作为GitLab管理员时在搭建完GitLab CE后&#xff0c;如何对其进行配置或设置 更改仓库存储位置&#xff1a;切换到root用户下操作 默认存放位置&…

SSL 证书

自动获取 Lets Encrypt 免费证书 &#xff08;适用于 Linux 系统&#xff09; 安装 Certbot sudo apt-get update sudo apt-get install certbot python3-certbot-nginx # Nginx 服务器 sudo apt-get install certbot python3-certbot-apache # Apache 服务器 获取和安装证…

小米rdemi红米ax3000t刷机 20240707最新配套完整程序整理合集

小米rdemi红米ax3000t刷机程序地址&#xff1a; https://www.123pan.com/s/LA1bVv-EOzVv.html 小米路由器SSH密码计算器 https://www.1234f.com/fuwu/ax3000t/ 最新更新地址&#xff1a;https://www.1234f.com/fuwu/openwrt/ 依次输入如下命令&#xff1a; curl -X POST h…

Leetcode 295.数据流的中位数

295.数据流的中位数 问题描述 中位数是有序整数列表中的中间值。如果列表的大小是偶数&#xff0c;则没有中间值&#xff0c;中位数是两个中间值的平均值。 例如 arr [2,3,4] 的中位数是 3 。例如 arr [2,3] 的中位数是 (2 3) / 2 2.5 。 实现 MedianFinder 类: Media…