websocket集群部署遇到的一些事

news2024/11/20 2:45:15

最近刚好有个场景,业务处理一份报告需要关注实时处理的进度。

本来打算使用前端轮训方式,但是考虑到这样效率比较低,也无法精确知道处理进度,就想到用websocket和前端实时交互,进度有更新就通知前端,避免了无用的空轮训请求。

websocket通过session链接,和前端保持链接,将客户的信息存储在内存中,用户每个请求都有一个唯一的uuid,后续通过uuid查找对应客户的session发送websocket。

系统架构图

流程图

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author 
 * @since 2024/8/26
 */
@Configuration
@EnableAutoConfiguration
@EnableWebSocket
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author 
 * @since 2024/8/26
 */
@ServerEndpoint(value = "/websocket/progress")
@Component
@Slf4j
public class WebSocketServer {

    //静态变量,记录当前在线连接数
    private static int onlineCount = 0;
    //存储客户端对应的WebSocket对象
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet();
    //与某个客户端的连接会话
    private Session session;
    //客户端sid
    private String sid = "";

    //建立连接成功后调用的方法
    @OnOpen
    public void onOpen(Session session) {
        //获取当前会话
        this.session = session;

        //存储WebSocket对象
        String sid = getUuid();
        //客户端标识
        this.sid = sid;
        webSocketSet.add(this);
        //在线连接计数
        addOnlineCount();
        try {
            //调用服务端发送信息方法
            sendMessage("连接成功" + this.sid);
        } catch (Exception e) {
            log.error("IO异常:{} ", e);
        }
    }

    private String getUuid() {
        IdWorker idWorker = new IdWorker();
        long l = idWorker.nextId();
        return String.valueOf(l);
    }

    @OnClose
    public void onClose() {
        //移除WebSocket对象
        webSocketSet.remove(this);
        subOnlineCount();
    }

    @OnMessage
    public void onMessage(String message) {
        log.info("WebSocketServer 收到message{}", this.sid);
    }

    @OnError
    public void onError(Throwable error) {
        log.error("onError,{}", error);
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public static void sendInfo(String message, String sid) {
        if (sid == null) {
            return;
        }
        for (WebSocketServer item : webSocketSet) {
            try {
                if (item.sid.equals(sid)) {
                    //发给单一用户
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                log.error("发送websocket失败{}", e);
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount += 1;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount -= 1;
    }
}

和前端本地测试的好好的,一点毛病没有,以为万事大吉了。

结果一发布到测试环境,gg了,进度条时好时坏,一开始还没查出来问题,没有往集群的方面想,还以为是哪里不小心自动关闭了websocket,查了n久之后,突然顿悟了,有集群啊,多台服务器,前端链接之后,session是保持了,但是一开始链接的可能是服务器A,后续的请求可能就到服务器B了,那就拿不到客户的session,就无法发送消息了。

找到问题,就得想解决方法了

方法一 固定ip请求,用nginx的ip_hash负载均衡请求,可惜行不通,问了运维那边,ip是动态了,搞不定。

方法二 改网关的负载均衡策略,因为原来的负载均衡策略是均衡分发,改到源地址策略,就会固定ip请求到同一服务器了,和nginx ip_hash类似,但是也行不通,不能改策略。

方法三 最后只剩这个方法,通过rabbitmq广播各个服务器,根据服务器本身存储的session信息(uuid),判断这个消息时候需要处理。

customer自己发送消息,自己监听,发送mq的时候会自动在头尾带上",以及会对字符串中的“引号进行转义,加上\,所以都要进行替换


import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author 
 * @since 2024/9/19
 */
@Component
public class WebsocketMqCustomer {

    @RabbitListener(queues = "#{psQueue.name}")
    public void pubsubMqMsg(String message) throws IOException {
        String[] xxxx = message.replace("\\", "").split(";;;");
        WebSocketServer.sendInfo(xxxx[1].substring(0, xxxx[1].length() - 1).replace("\\", ""), xxxx[0].substring(1));
    }

    public static void sendInfo(String message, String sid) {
        if (StringUtils.isNotEmpty(sid)) {
            RabbitTemplate rabbitTemplate = SpringUtil.getBean(RabbitTemplate.class);
            rabbitTemplate.convertAndSend(MainRabbitMqCreateConfig.EXCHANGE_WEBSOCKET, sid, sid + ";;;" + message);
        }
    }
}

还遇见了一个问题,@ServerEndpoint注解添加的类无法通过@Autowired直接注入,因为websocket处理是基于Servlet的,而Servlet容器并不像Spring mvc那样自动管理bean的依赖注入,可以通过Application来获取其他bean。

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

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

相关文章

【Qt】系统相关学习--底层逻辑--代码实践

Qt事件 基本概念 理解Qt事件 事件是用户与应用程序之间交互的基础。它允许应用程序对用户的输入做出响应&#xff0c;例如鼠标点击一下又或者用户键盘输入相应内容。也就是说每一次用户与应用程序交互的时候&#xff0c;都会产生一个事件&#xff0c;然后传递给相应的控件或者…

如何制作小程序商城

在这个瞬息万变的数字时代&#xff0c;每一个商业决策的背后都蕴含着对市场趋势的深刻洞察与把握。随着移动互联网的飞速发展&#xff0c;小程序商城以其便捷性、高效性和低门槛的特点&#xff0c;正逐步成为众多企业商家竞相布局的营销新阵地。今天&#xff0c;就让我们一起深…

sql-server【bcp工具】

目录 1.查看bcp是否可用 2.bcp 命令的基本语法 3.数据导出 4.数据导入 bcp&#xff08;Bulk Copy Program&#xff09;是 SQL Server 提供的一个命令行工具&#xff0c;用于在 SQL Server 实例与用户指定格式的数据文件之间批量复制表或视图数据。bcp 工具非常适合进行大量…

雷池 WAF 如何配置才能正确获取到源 IP

经常有大哥反馈说雷池攻击日志里显示的 IP 有问题。 这里我来讲一下为什么一些情况下雷池显示的攻击 IP 会有问题。 问题说明 默认情况下&#xff0c;雷池会通过 HTTP 连接的 Socket 套接字读取客户端 IP。在雷池作为最外层网管设备的时候这没有问题&#xff0c;雷池获取到的…

康谋分享 | 数据隐私和匿名化:PIPL与GDPR下,如何确保数据合规?(一)

目录 一、自动驾驶数据合规挑战 二、PIPL和GDPR的异同点 1、覆盖范围 2、个人信息定义 3、敏感数据 自动驾驶技术的快速发展伴随着数据隐私保护的严峻挑战。中国《个人信息保护法》&#xff08;PIPL&#xff09;与欧盟《通用数据保护条例》&#xff08;GDPR&#xff09;为…

Unity实战案例全解析:RTS游戏的框选和阵型功能(4)阵型功能

前篇&#xff1a;Unity实战案例全解析&#xff1a;RTS游戏的框选和阵型功能&#xff08;3&#xff09;生成范围检测框 重置框选操作-CSDN博客 本案例来源于unity唐老狮&#xff0c;有兴趣的小伙伴可以去泰克在线观看该课程 我只是对重要功能进行分析和做出笔记分享&#xff0c;…

PCIE XDMA

1 硬件电路 2 XDMA IP说明 2.1 Basic Mode:配置模式,选择 Basic 配置模式就可以了,Advanced 高级配置一般用不到; Lane Width:链路宽度,对于 ZYNQ MPSOC开发板,选择x2; Max Link Speed:最大链路速度,选择 8.0GT/s,即 PCIe 3.0 的传输速率; Refere

ultralytics yolo v8 示例:加载官方模型进行推理

Ultralytics YOLO 是计算机视觉和 ML 领域专业人士的高效工具。 安装 ultralytics 库&#xff1a; pip install ultralytics 实现代码如下&#xff1a; import cv2 from ultralytics import YOLO# 加载预训练的 YOLOv8n 模型 ckpt_dir "./ckpt/" # 模型缓存地址…

3、练习常用的HBase Shell命令+HBase 常用的Java API 及应用实例

目录 &#xff08;一&#xff09;练习常用的HBase Shell命令1、启动HBase2、练习shell命令create scan list describe alterputgetdeletedrop 关于NoSQL数据库中的列族和列3、关闭hbase服务 &#xff08;二&#xff09;HBase 常用的Java API 及应用实例1、启动hbase服务2、启动…

C++版本更新历史

前言 C语言发展至今已经迭代了很多版本&#xff0c;而在不同环境中编写代码时经常看到C标准的设定&#xff0c;比如 Leetcode 中可以看到版本信息&#xff1a; 这说明Leetcode已经支持最新C23标准了&#xff0c;但某些环境并不一定支持这些语法&#xff0c;如果不清楚使用的语法…

提示词工程 (Prompt Engineering) 最佳实践

prompt Engineering 概念解析 提示工程是一门较新的学科&#xff0c;关注提示词开发和优化&#xff0c;帮助用户将大语言模型&#xff08;Large Language Model, LLM&#xff09;用于各场景和研究领域。研究人员可利用提示工程来提升大语言模型处理复杂任务场景的能力&#xf…

Jetson nano (4GB版本)跑yolov8n(TensorRT 加速)网络

大家好&#xff0c;我是王硕&#xff0c;项目原因需要在Jetson nano平台上跑yolov8s &#xff0c;需要使用TensorRt加速&#xff0c;看了网上的教程&#xff0c;写的太差了&#xff0c;资料零零散散的&#xff0c;故详细介绍一下步骤。 如果想使用jetson Nano平台部署yolov8&a…

Selenium与数据库结合:数据爬取与存储的技术实践

目录 一、Selenium与数据库结合的基础概念 1.1 Selenium简介 1.2 数据库简介 1.3 Selenium与数据库结合的优势 二、Selenium爬取数据的基本步骤 2.1 环境准备 2.2 编写爬虫代码 2.3 数据提取 2.4 异常处理 三、数据存储到数据库 3.1 数据库连接 3.2 数据存储 3.3 …

windows10录屏工具,四款新手必备软件!

今天要和大家聊的是-——win10的录屏工具。在Win10电脑上&#xff0c;那些让我们爱不释手的录屏神器有很多&#xff0c;不管是哪个行业的人&#xff0c;录屏软件简直是日常工作的得力助手&#xff0c;比如说对于程序员来说&#xff0c;不管是分享代码教程、记录bug复现&#xf…

RKLLM部署

RKLLM 写在前面&#xff1a;建议去阅读官方提供的RKLLM doc&#xff0c;本文基于官方的RKLLM doc制作而成&#xff08;没有将flask相关内容添加进来&#xff09;&#xff0c;仅仅添加了完整流程的执行过程截图和在做这以流程过程中遇到的问题 RKLLM可以帮助用户快速将人工智能…

【vite】搭建完整项目流程、项目配置

文章目录 完整项目源码项目技术栈&#xff1a;项目地址&#xff1a; 一、创建项目二、安装scss三、安装路由router四、项目配置 参考文章&#xff1a;vite搭建完整项目 完整项目源码 觉得创建太麻烦就直接从github克隆吧&#xff0c;https://github.com/fruge365/vite-common…

金智维KRPA之Excel自动化

Excel自动化操作概述 Excel自动化主要用于帮助各种类型的企业用户实现Excel数据处理自动化&#xff0c;Excel自动化是可以从单元格、列、行或范围中读取数据&#xff0c;向其他电子表格或工作簿写入数据等活动。 通过相关命令&#xff0c;还可以对数据进行排序、进行格式…

开发者如何自主绑定和解除小程序和公众号长期/短期运营者微信号?

开发者如何自主绑定和解除小程序和公众号长期/短期运营者微信号&#xff1f; 1、什么是长期/短期运营者微信号&#xff1f; 为了让更多人管理公众号更方便与安全&#xff0c;每个公众号可由管理员添加绑定5个长期运营者微信号、20个短期运营者微信号&#xff0c;运营者微信号…

【sw2024】solidworks2024双击setup.exe无反应管理员运行也没反应解决方法

第一步 官网下载xxclean&#xff0c;打开xxclean最新版本&#xff0c;登录。官网xxclean.com或者自己浏览器搜。 第二步 点击扩展功能&#xff0c;点击 运行sw2024安装程序无反应 右边的开始 第三步 进度百分之百之后去双击setup就有界面了。

Tusi.Art:AI模型,comfyui工作流,一键同款!

前言 Tusi.Art&#xff1a;AI模型分享与在线运行平台的革命性体验 在当今迅速发展的AI绘图领域&#xff0c;模型的分享与应用变得尤为重要。Tusi.Art 就是这样一个平台&#xff0c;为用户提供了分享和在线运行 AI 模型的便捷服务。不仅如此&#xff0c;平台还具备强大的功能&…