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

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

之前几篇文章,我们讲了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 {

    // 输入通道拦截器
    @Resource
    private InboundChannelInterceptor inboundChannelInterceptor;

    // 请求头认证信息使用
    @Resource
    private PrincipalHandshakeHandler principalHandshakeHandler;

    /**
     * <b>功能描述:</b>注册STOMP协议的节点(endpoint),并映射指定的url<br>
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册一个STOMP的endpoint,并指定使用SockJS协议
        registry.addEndpoint("/ws")
                .setHandshakeHandler(principalHandshakeHandler)
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }


    /**
     * <b>功能描述:</b>配置消息代理(Message Broker)<br>
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //点对点应配置一个/user消息代理,广播式应配置一个/topic消息代理,群发(mass),单独聊天(queue)
        //推送消息前缀
        registry.enableSimpleBroker("/topic");
        //点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是/user/
        // 应用请求前缀
        // 推送用户前缀
        registry.setUserDestinationPrefix("/user");
    }
    
     /**
     * <b>功能描述:</b>输入通道配置<br>
     */
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(this.inboundChannelInterceptor);// 设置拦截器
        registration.taskExecutor()    // 线程信息
                .corePoolSize(10)     // 核心线程池
                .maxPoolSize(20)      // 最多线程池数
                .keepAliveSeconds(60); // 超过核心线程数后,空闲线程超时60秒则杀死
    }

       /**
     * <b>功能描述:</b>消息传输参数配置<br>
     */
    @Override
    public 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 {

    // 后端实现
    @Resource
    private IWebSocketService webSocketServiceImpl;

    @SneakyThrows
    @Override
    public 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";


    @Override
    protected 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 {

    @Override
    public 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);
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }
}

​​​​​​​

图片

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新产品成功的七大关键要素:理论解析与案例探讨

在激烈的市场竞争中&#xff0c;新产品的成功推出不仅关乎企业的生死存亡&#xff0c;更是企业持续发展的核心动力。那么&#xff0c;新产品如何能够脱颖而出&#xff0c;赢得市场的青睐呢&#xff1f;本文将深入探讨新产品成功的七大关键要素&#xff0c;并结合实际案例进行解…

中颖51芯片学习8. ADC模数转换

中颖51芯片学习8. ADC模数转换 一、ADC工作原理简介1. 概念2. ADC实现方式3. 基准电压 二、中颖芯片ADC功能介绍1. 中颖芯片ADC特性2. ADC触发源&#xff08;1&#xff09;**软件触发**&#xff08;2&#xff09;**TIMER4定时器触发**&#xff08;3&#xff09;**外部中断2触发…

洛谷P1057 [NOIP2008 普及组] 传球游戏

#include<iostream> using namespace std; int n;// n个人传球游戏 默认开始球在编号为1的位置 int m;// 传递m次球 int main(){cin>>n>>m;// 动态转方程&#xff1a;// 球传递到编号为k人的手中// 种类总数 传递到k-1编号种类总数 传递到k1编号种类总数//…

如何查看微信公众号发布文章的主图,如何看微信文章的主图,怎么才能拿到主图

如何查看&#xff0c;微信公众号发布文章的主图&#xff0c;如何看微信文章的主图 起因是这样的&#xff0c;当我看到一篇文章的时候&#xff0c;他的主图很漂亮&#xff0c;但是&#xff0c;正文里没有&#xff0c;而我又想看到&#xff0c;并且使用这张图片&#xff0c;该怎么…

代码随想录训练营Day 27|Python|Leetcode|122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能获…

同旺科技 USB TO SPI / I2C适配器读写24LC256--页写

所需设备&#xff1a; 1、USB 转 SPI I2C 适配器&#xff1b;内附链接 2、24LC256芯片 适应于同旺科技 USB TO SPI / I2C适配器升级版、专业版&#xff1b; 从00地址开始写入64个字节&#xff0c;然后再将64个字节读回&#xff1b; 页写时序&#xff1a; 读时序&#xff1a…

easyx库的介绍

前言 如果想要摆脱黑窗口的限制那么easyx图形库是一个好的选择 easyx的初认识 easyx是针对c的图形库&#xff0c;可以帮助c/c上手图形和游戏编程 所以要用easyx必须要用.cpp的后缀 1 easyx的原理 window的图形编程&#xff0c;最终都由window的底层API来实现 2 easyx的颜色 …

【Java笔记】第4章:深入学习循环结构

前言1. 循环的理解2. while循环3. do...while循环4. for循环5. 循环的控制语句6. 循环的嵌套结语 ↓ 上期回顾: 【Java笔记】第3章&#xff1a;深入学习分支结构 个人主页&#xff1a;C_GUIQU 归属专栏&#xff1a;【Java学习】 ↑ 前言 各位小伙伴大家好&#xff01;上期小编…

Mac下删除旧版本.net sdk

参照微软官网给的方法,Releases dotnet/cli-lab (github.com) 好像不能直接的解决问题,我做一下补充,希望对需要删除旧版本sdk的小伙伴们有所帮助 1:下载工具包 Releases dotnet/cli-lab (github.com) 2:打开终端,cd切换到该文件的制定目录 3:然后按照提示一步步执行…

2024上海国际半导体制造设备材料与核心部件展览会

2024上海国际半导体制造设备材料与核心部件展览会 2024 Shanghai International Semiconductor Manufacturing Equipment Materials and Core Components Exhibition 时间&#xff1a;2024年11月18日-20日 地点&#xff1a;上海新国际博览中心 详询主办方陆先生 I38&#…

【干货精品分享】Elasticsearch 6.7 Should 子语句的失效

在ES 使用多条件 查询&#xff0c;并且是多个条件只需要满足部分条件的时候&#xff0c;我们通常会使用到ES的should查询 GET /trademark_query_index/_search {"query":{"bool" : {"must":[{"match" : {"origin": {"…

PACS系统源码 新一代的医学图像管理系统 pacs 云影像,PACS云胶片,PACS影像工作站系统源码

PACS系统源码 新一代的医学图像管理系统 pacs 云影像,PACS云胶片,PACS影像工作站系统源码 三甲医院医学影像PACS系统源码&#xff0c;集成三维影像后处理功能&#xff0c;包括三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜、最大/小密度投影、心脏动脉钙化分…

SynchronousQueue

SynchronousQueue 解释&#xff1a; 同步队列 介绍 实现了BlockingQueue 和 Queue 其中每个插入操作必须等待另一个线程相应的删除操作 同步队列没有任何容量&#xff0c;甚至没有一个容量 如果你想放入一个元素&#xff0c;就必须有另外一个线程尝试从中移除元素 使用 …

软件行业中的蓝海领域有哪些?

什么是蓝海&#xff1f; 蓝海&#xff0c;指的是未知的市场空间。这个概念相对于“红海”而言&#xff0c;红海则是指已知的市场空间。 企业要启动和保持获利性增长&#xff0c;就必须超越产业竞争&#xff0c;开创全新市场&#xff0c;这其中包括两块&#xff1a;一块是突破…

Shader 渐变屏幕

渐变 前置工作&#xff0c;创建缓冲&#xff0c;对顶点着色器传递顶点数据 function main() {var canvas document.getElementById(webgl);var gl getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) returnvar n initVertexBuffers(gl); }fu…

多个路由器连接的PC端进行ping通信需要做的事

实验环境&#xff1a; 三台PC三台路由器&#xff0c;并且配置好IP 拓扑图&#xff1a; 需求描述&#xff1a; 在PC0进行与PC2的ping通信&#xff1a; 需求步骤&#xff1a; 1.1首先配置ip&#xff08;略过&#xff09; 1.2我们首先查看在只配置了IP的情况下&#xff0c;P…

小程序如何优化搜索排名,获取曝光

在移动互联网时代&#xff0c;小程序以其便捷、轻量级的特点&#xff0c;逐渐成为用户获取服务的重要渠道。然而&#xff0c;小程序数量众多&#xff0c;如何让自己的小程序在搜索中脱颖而出&#xff0c;获取更多的曝光和流量&#xff0c;成为众多开发者关注的焦点。 一、理解…

javascript遍历多层级数据

javascript遍历多层级数据 代码 // data:需要处理的数据 level:用于标记数据所在层级(从1开始) const dataLoop(data, level 1)>{return data.map(item>{let r {...item, level}console.log(item, item)// 判断如果有下级&#xff0c;就传入children继续向下循环if(r…

Vuex 的原理

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store&#xff08;仓库&#xff09;。“store” 基本上就是一个容器&#xff0c;它包含着你的应用中大部分的状态 ( state )。 Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的…

在Vue项目使用kindEditor富文本编译器以及上传图片

第一步 npm install kindeditor第二步&#xff0c;建立kindeditor.vue组件 <template><div class"kindeditor"><textarea :id"id" name"content" v-model"outContent"></textarea></div> </templa…