微服务使用SockJs+Stomp实现Websocket 前后端实例 | Vuex形式断开重连、跨域等等问题踩坑(一)

news/2024/11/30 18:53:00/

大家好,我是程序员大猩猩。

之前几篇文章,我们讲了Spring Cloud Gateway的轻量级实现,Nginx的配置概念与实现,如以下往期文章。

轻量级的Spring Cloud Gateway实践,实现api和websocket转发
轻松实现Nginx的HTTP与WebSocket转发:你的网站需要这个!

以上我们提到了SockJs和Stomp,对于Gateway与SockJs的转发连接友好性,那么我们今天就来通过实践来完成这些实例。

首先,我们来了解一下SokeJs和Stomp。

什么是 SockJS

SockJS 是一种浏览器与服务器之间的通信协议,它可以在浏览器和服务器之间建立一个基于 HTTP 的双向通信通道。SockJS 的主要作用是提供一种 WebSocket 的兼容性解决方案,使得不支持 WebSocket 的浏览器也可以使用 WebSocket。

当浏览器不支持 WebSocket 时,SockJS 会自动切换到使用轮询(polling)或长轮询(long-polling)的方式进行通信。

在使用 SockJS 时,首先需要在客户端和服务器端分别引入 sockjs-client.js 和 sockjs-server,然后在客户端通过 new SockJS(url) 的方式建立一个 SockJS 连接。

客户端和服务器端之间的通信是基于事件的,当客户端发送消息时,服务器端会触发一个 onmessage 事件,然后将消息发送回客户端。客户端在接收到消息后,会触发一个 onmessage 事件,然后处理收到的消息。

我们可以在前端代码中使用以下语句来实例化它:

new SockJS('http://*****:8080/ws/user'); // 连接后端接口

什么是 Stomp

STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。

同样,我们怎么初始化使用它:

var url = "ws://*****:8080/ws/user";
var client = Stomp.client(url);
后端实现

当我们工程项目创建好之后,pom内直接引入:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
首先,我们来完成配置类:
@Configuration
// 注解开启使用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
@EnableWebSocketMessageBroker
public class WebScoketConfig implements WebSocketMessageBrokerConfigurer {// 输入通道拦截器@Resourceprivate InboundChannelInterceptor inboundChannelInterceptor;// 请求头认证信息使用@Resourceprivate PrincipalHandshakeHandler principalHandshakeHandler;/*** <b>功能描述:</b>注册STOMP协议的节点(endpoint),并映射指定的url<br>*/@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {//注册一个STOMP的endpoint,并指定使用SockJS协议registry.addEndpoint("/ws").setHandshakeHandler(principalHandshakeHandler).setAllowedOriginPatterns("*").withSockJS();}/*** <b>功能描述:</b>配置消息代理(Message Broker)<br>*/@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {//点对点应配置一个/user消息代理,广播式应配置一个/topic消息代理,群发(mass),单独聊天(queue)//推送消息前缀registry.enableSimpleBroker("/topic");//点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是/user/// 应用请求前缀// 推送用户前缀registry.setUserDestinationPrefix("/user");}/*** <b>功能描述:</b>输入通道配置<br>*/@Overridepublic void configureClientInboundChannel(ChannelRegistration registration) {registration.interceptors(this.inboundChannelInterceptor);// 设置拦截器registration.taskExecutor()    // 线程信息.corePoolSize(10)     // 核心线程池.maxPoolSize(20)      // 最多线程池数.keepAliveSeconds(60); // 超过核心线程数后,空闲线程超时60秒则杀死}/*** <b>功能描述:</b>消息传输参数配置<br>*/@Overridepublic void configureWebSocketTransport(WebSocketTransportRegistration registration) {registration.setSendTimeLimit(15 * 1000)    // 超时时间.setSendBufferSizeLimit(512 * 1024) // 缓存空间.setMessageSizeLimit(128 * 1024);   // 消息大小}
}

@EnableWebSocketMessageBroker表示启用Socket代理。

registerStompEndpoints方法内addEndpoint表示接口前缀,当前端连接时,使用http://****:8080/ws方式接入。

setHandshakeHandler添加认证请求头的认证类。

setAllowedOriginPatterns跨域处理

withSockJS是注册SockJS代理

拦截器实现

@Slf4j
@Component
public class InboundChannelInterceptor implements ChannelInterceptor {// 后端实现@Resourceprivate IWebSocketService webSocketServiceImpl;@SneakyThrows@Overridepublic Message<?> preSend(Message<?> message, MessageChannel channel) {StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);if (accessor == null) {log.error("accessor is null");return message;}StompCommand stompCommand = accessor.getCommand();String simpSessionId = accessor.getHeader("simpSessionId").toString();String userId = accessor.getFirstNativeHeader("userId");if (StompCommand.CONNECT.equals(stompCommand)) {this.webSocketServiceImpl.connect(simpSessionId, userId);} else if (StompCommand.DISCONNECT.equals(stompCommand)) {this.webSocketServiceImpl.disconnect(simpSessionId);} else if (StompCommand.SEND.equals(stompCommand)) {this.webSocketServiceImpl.ping(simpSessionId, userId);}return message;}
}

认证信息类

@Slf4j
@Component
public class PrincipalHandshakeHandler extends DefaultHandshakeHandler {/*** <b>功能描述:</b>请求头<br>*/public static final String ACCESS_TOKEN = "token";@Overrideprotected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {/*** 这边可以按需求,如何获取唯一的值,既unicode* 得到的值,会在监听处理连接的属性中,既WebSocketSession.getPrincipal().getName()* 也可以自己实现Principal()*/if (request instanceof ServletServerHttpRequest) {ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;HttpServletRequest httpRequest = servletServerHttpRequest.getServletRequest();/*** 携带参数,你可以cookie,请求头,或者url携带,这边我采用url携带*/String header = httpRequest.getHeader(ACCESS_TOKEN);log.info("token:{}", header);final String token = httpRequest.getParameter(ACCESS_TOKEN);if (StrUtil.isEmpty(token)) {return null;}return () -> token;}return null;}
}

​​​​​​​我们使用Dug模式启动服务看看是否完成,并看看它的Mappings列表。

图片

我们本地输入链接查看,部署成功。

图片

踩坑问题:

1.setAllowedOriginPatterns跨域请求只是一个小点,因为SockJs会封装一个sock-node/info?t=...的接口,我们还必须要全局的设置跨域。

另外网络其他博客,很多会说这个接口404的问题,然后注释掉socket-client什么node_modules js内的1600的行代码。

我是真不信,最后我把我后端代码跨域处理后,就可用了,后端这个接口是默认开放的。有些东西我们真的不要信。

@Component
public class SimpleCORSFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) res;response.setHeader("Access-Control-Allow-Credentials", "true");response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD,PUT");response.setHeader("Access-Control-Max-Age", "3600");response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With, token");HttpServletRequest request = (HttpServletRequest) req;if ("OPTIONS".equals(request.getMethod())) {response.setStatus(HttpServletResponse.SC_OK);return;}chain.doFilter(req, res);}@Overridepublic void init(FilterConfig filterConfig) {}@Overridepublic void destroy() {}
}

​​​​​​​

图片

成功,下节我们来看看前端Vue的实现,再见!!!


http://www.ppmy.cn/news/1425426.html

相关文章

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…

多模态AnyGPT——整合图像、语音和文本多模态大规模语言模型算法原理与实践

概述 大规模语言模型在理解和生成人类语言方面具有非凡的能力&#xff0c;但迄今为止&#xff0c;它们的能力主要局限于文本处理。然而&#xff0c;现实世界是一个多模式的环境&#xff0c;信息通过视觉、听觉和触觉等多种感官进行交换。融入这种多样性是开发下一代系统的主要…

spring boot后端开发基础

spring boot后端开发基础 Spring Boot一、开发步骤二、Web分析三、跨域问题四、HTTP协议五、Web服务器六、响应前端请求七、springboot常用注解创建一个简单的RESTful API服务层和数据访问层配置类和Bean定义响应体和路径变量 Spring Boot 一、开发步骤 创建项目 添加依赖 项…

MySQL 按时间进行表分区

文章目录 创建按月份分区的表开启数据库事件临时配置&#xff08;服务重启会失效&#xff09;修改my.cnf文件&#xff08;推荐&#xff09; 定时事件自动建分区查询表分区信息 创建按月份分区的表 create table if not exists table_name (id bigint auto_increment …

centos 下如何锁定docker版本

可能因为某些原因无意间执行了yum update或者apt-get -y upgrade;导致Docker版本升级&#xff0c;这会引起你开发的项目因为环境不同而面临不能执行的风险&#xff0c;为了避免此类问题发生&#xff0c;建议在安装好Docker后对Docker软件进行锁定&#xff0c;防止Docker意外更新…

鸢尾花数据集的KNN探索与乳腺癌决策树洞察

鸢尾花数据集的KNN探索与乳腺癌决策树洞察 今天博主做了这个KNN和决策树的实验。 一.数据集介绍 介绍一下数据集&#xff1a; 威斯康星州乳腺癌数据集&#xff1a; 威斯康星州乳腺癌数据集&#xff08;Wisconsin Breast Cancer Dataset&#xff09;是一个经典的机器学习数…

如何解决热key问题

什么是热key问题&#xff1f;就是一个key的数据被大量访问&#xff0c;导致key所在节点的redis服务器承受巨大的压力甚至导致服务器直接崩了&#xff0c;这种情况如何解决了&#xff1f; 1.缓存预热&#xff1a; 通过在业务刚启动或者在业务的低峰期的时候&#xff0c;直接经常…

PSO-GPR单变量时序预测-递归预测未来数据 基于粒子群算法-高斯过程回归递归预测未来数据

文章目录 效果一览文章概述订阅专栏只能获取一份代码部分源码参考资料效果一览 文章概述 PSO-GPR单变量时序预测-递归预测未来数据 基于粒子群算法-高斯过程回归递归预测未来数据 订阅专栏只能获取一份代码 部分源码 %