IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 若依springcloud gateway nacos 集成websocket -> 正文阅读

[Java知识库]若依springcloud gateway nacos 集成websocket

一.代码部分

相应模块里面添加3个类

WebSocketConfig,MyWebSocketHandler,WebSocketInterceptor

1.WebSocketConfig

package com.ruoyi.config;

import com.ruoyi.handler.MyWebSocketHandler;
import com.ruoyi.interceptor.WebSocketInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * 首先注入一个ServerEndpointExporterBean,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private static final Logger log = LoggerFactory.getLogger(WebSocketConfig.class);

    @Autowired
    private MyWebSocketHandler myWebSocketHandler;

    @Autowired
    private WebSocketInterceptor webSocketInterceptor;

    @Value("#{'${websocket.wsHandlers}'.split(',')}")
    private String[] paths;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myWebSocketHandler, paths)
                .setAllowedOrigins("*")
                .addInterceptors(webSocketInterceptor);
    }
}

2.?MyWebSocketHandler

package com.ruoyi.handler;

import com.ruoyi.common.WebSocketCommon;
import com.ruoyi.common.core.constant.WebSocketConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;

@Component
public class MyWebSocketHandler implements WebSocketHandler {

    private static final Logger log = LoggerFactory.getLogger(MyWebSocketHandler.class);

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        log.info("connect websocket successful!");
        Object userId = session.getAttributes().get(WebSocketConstants.TOKEN);
        if(userId==null){
            return;
        }
        WebSocketSession oldSession = WebSocketCommon.CLIENT_SESSION.get(WebSocketCommon.getMapKey(session));
        if (oldSession != null) {
            log.info("close original session-start");
            try {
                oldSession.close();
            } catch (IOException e) {
                log.info("close original session failed");
            }
        }
        //新的会话放入
        WebSocketCommon.CLIENT_SESSION.put(WebSocketCommon.getMapKey(session),session);
    }

    /**
     * 接收客户端发送的消息-用作客户端心跳
     * @param session
     * @param message
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message){
        log.info("handle message start");
        try {
            //String mess = (String) message.getPayload();  //获取客户端发送的消息
            //这边可能需要处理更新map里session机制,防止map里面保存的失效,待定,等后面实际运行观察
            if(session.isOpen()){
                //心跳响应包
                session.sendMessage(new PongMessage());
            }
        } catch (Exception e) {
            log.error("e", e);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.info("handle message start");
        if (session.isOpen()) {
            session.close();
        }
        log.error("connect error", exception);
        Object userid = session.getAttributes().get(WebSocketConstants.TOKEN).toString();
        if(userid==null){
            return;
        }
        WebSocketCommon.CLIENT_SESSION.remove(WebSocketCommon.getMapKey(session));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus){
        log.error("在线人数: {}" + WebSocketCommon.CLIENT_SESSION.size());
        log.error("connection closed: " + closeStatus);
        WebSocketCommon.CLIENT_SESSION.remove(WebSocketCommon.getMapKey(session));
        log.error("在线人数: {}" + WebSocketCommon.CLIENT_SESSION.size());
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

3.WebSocketInterceptor

package com.ruoyi.interceptor;

import com.ruoyi.common.core.constant.WebSocketConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

@Component
public class WebSocketInterceptor implements HandshakeInterceptor {

    private static final Logger log = LoggerFactory.getLogger(WebSocketInterceptor.class);

    //在握手之前执行该方法, 继续握手返回true, 中断握手返回false. 通过attributes参数设置WebSocketSession的属性
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes){
        if (request instanceof ServletServerHttpRequest) {
            String uri = request.getURI().getPath();
            String token = uri.substring(uri.lastIndexOf("/")+1);
            attributes.put(WebSocketConstants.TOKEN,token);
            log.info("current token is:"+token);
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        log.info("coming webSocketInterceptor afterHandshake method...");
    }

}

二.nacos中关键配置

在ruoyi-gateway-dev.yml 配置文件中添加如下,我是在ruoyi-collection这个模块中使用websocket的,所以配置路由到这个服务

        # websocket模块
        - id: ruoyi-websocket
          uri: lb:ws://ruoyi-collection
          predicates:
            - Path=/ws/**
        - id: ruoyi-websocket
          uri: lb:ws://ruoyi-collection
          predicates:
            - Path=/ws1/**  

这边是加了2个ws和ws1路径的配置,这样访问ws://ip+port/ws/xxx 和ws://ip+port/ws1/xxx都能连接上,这边注意

三.其它说明

1.在ruoyi-gateway-dev.yml中上面的配置中加了下面

filters:

- StripPrefix=1

这个配置,会导致连不上,要注意。

2.如果需要授权才能连接,我是这么实现的,我连接地址后面拼了一个token,例如ws://ip+port/ws/xxx?token=xxxxxx,这样在网关那边鉴权的时候,获取连接后面的token,拿到后正常往后面走,贴出关键代码如下

在gateway模块中

com.ruoyi.gateway.filter.AuthFilter#getToken

private String getToken(ServerHttpRequest request)
    {
        String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
        if(StringUtils.isEmpty(token)){
            //尝试从拼接参数中获取token,这步是为了websocket鉴权
            Object authorization = request.getQueryParams().get(TokenConstants.AUTHENTICATION);
            if(authorization!=null){
                token = ((List<?>) authorization).get(0).toString();
            }
        }
        // 如果前端设置了令牌前缀,则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
        {
            token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
        }
        return token;
    }

如果不需要授权的话,则在ruoyi-gateway-dev.yml中配置下白名单即可

  # 不校验白名单
  ignore:
    whites:
      - /auth/logout
      - /auth/login
      - /auth/register
      - /*/v2/api-docs
      - /csrf
      - /collection/sendBoxStatus
      - /ws/**
      - /ws1/**

3.这边补上上面代码使用到的新加的类,供参考

WebSocketConstants

package com.ruoyi.common.core.constant;

public class WebSocketConstants {

    //用户标识
    public static String CLIENT_FLAG = "clientId";

    //用户标识key
    public static String TOKEN = "token";

    //每个连接key前缀标识
    public static String PREFIX = "prefix";
}
WebSocketCommon
package com.ruoyi.common;

import com.ruoyi.common.core.constant.WebSocketConstants;
import org.springframework.web.socket.WebSocketSession;

import java.util.concurrent.ConcurrentHashMap;

public class WebSocketCommon {

    /**
     * 保存已登录的会话session
     */
    public static ConcurrentHashMap<String, WebSocketSession> CLIENT_SESSION = new ConcurrentHashMap();

    public static String getMapKey(WebSocketSession session){
        Object userId = session.getAttributes().get(WebSocketConstants.TOKEN);
        if(userId==null){
            return null;
        }
        String useridStr = userId.toString();
        return useridStr;
    }
}

最后非常感谢这篇文章的作者,参照这篇文章实现了功能,另外有什么问题欢迎留言,一起探讨。

SpringCloud Gateway 集成 WebSocket服务_哦哈哟小朋友的博客-CSDN博客_gateway整合websocket

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:36:15  更:2022-07-03 10:39:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:53:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码