Spring gateway websocket自定义负载均衡

news2024/12/24 10:27:56

业务需求
公司IM服务主要基于netty实现websocket,为保证在线用户channel通道畅通故一直使用单机运行。现由于公司业务增加需要增加IM集群,由于channel通道不能缓存,故急需一套可以完整兼容之前功能的方案。

技术选型
1、采用spring websocket方案,发送进行MQ广播,各个IM服务节点收到广播进行业务处理。
该种方式需要将原始功能重做,而且广播消息会增加系统额外开销,实现难度不大,开发成本较高。
2、gateway自定义负载均衡,当接收到ws消息直接根据用户ID进行路由。
该方式可以完美兼容原始功能,原始功能采用netty进行开发websocket,实现难度简单,开发成本低。
结论:为完美兼容老系统功能,且节省开发成本,本次迭代采用第二种方案。

开发原理
1、websocket 注册、心跳、发消息都会调用ws路径,且路径带有发送人ID、接收人ID,如果是注册消息只有发送人,对聊消息会存在接收人,心跳连接也会有发送人。故可以通过对 人员ID % 服务数量 将满足规则的ws请求路由到指定的服务上。
2、spring gateway 网关大家都应该不陌生,我们查看源码发现底层采用负载均衡过滤器实现的负载。
源码如下:
在这里插入图片描述

其中有一个受保护的方法用来选择路由到哪个服务提供者。
源码如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/ec44031ef2244c87ac221a0b21ce44a2.png

查看该方法我们可以看见内部调用了ribbon,ribbon根据配置的策略进行服务选择。
源码如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/f87f87ff5acd4152bff9c8d741834148.png

所以,我们可以覆写该选择服务的方法,如果是ws 请求直接进行自定义选择服务,其他请求放行执行默认逻辑。

小试牛刀
我们只是简单模拟ws 请求根据请求人员ID进行负载均衡,当然并没有考虑服务宕机如何切换路由等问题。如有需要建议使用hash环来解决这一问题,感兴趣的同学可以考虑使用hash环保存服务提供者信息。

1、提供自定义负载均衡过滤器类,该类继承默认的负责均衡过滤器

/**
 * 自定义负载均衡
 * @author senfel
 * @version 1.0
 * @date 2023/1/9 15:39
 */
public class ILoadBalancerClientFilter extends LoadBalancerClientFilter {


    /**
     * 发送人ID
     */
    public static final String SENDER_ID = "senderId";
    /***
     * 收件人ID
     **/
    public static final String RECEIVER_ID = "receiverId";

    /**
     * 发现客户端
     */
    private DiscoveryClient discoveryClient;


    public ILoadBalancerClientFilter(LoadBalancerClient loadBalancer,DiscoveryClient discoveryClient) {
        super(loadBalancer);
        this.discoveryClient = discoveryClient;
    }


    /**
     * 自定义服务实体选择
     * @param exchange
     * @author senfel
     * @date 2023/1/9 15:44
     * @return org.springframework.cloud.client.ServiceInstance
     */
    protected ServiceInstance choose(ServerWebExchange exchange) {
        //获取请求url
        URI requestUrl = (URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        //获取host
        String serverId = requestUrl.getHost();
        //请求path
        String path = requestUrl.getPath();
        //如果请求包含
        if(path.contains("/ws")){
            List<ServiceInstance> instances = discoveryClient.getInstances(serverId);
            if(!CollectionUtils.isEmpty(instances)){
                //获取请求参数
                MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                Long sendId = null;
                Long receiverId = null;
                if(Objects.nonNull(queryParams.get(SENDER_ID))){
                     sendId = Long.valueOf(queryParams.get(SENDER_ID).get(0));
                }
                if(Objects.nonNull(queryParams.get(RECEIVER_ID))){
                    receiverId = Long.valueOf(queryParams.get(RECEIVER_ID).get(0));
                }
                int index = 0;
                if(null != sendId){
                    index = (int) (sendId % instances.size());
                }
                if(null != receiverId){
                    index = (int) (receiverId % instances.size());
                }
                return instances.get(index);
            }
        }
        return loadBalancer.choose(((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
    }

}

2、将自定义的负载均衡过滤器注入容器

/**
 * 负载均衡过滤器载入
 * @author senfel
 * @version 1.0
 * @date 2023/1/9 16:03
 */
@Configuration
public class LoadLalancerConfig {

    @Bean
    public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient loadBalancer, DiscoveryClient discoveryClient){
        return new ILoadBalancerClientFilter(loadBalancer,discoveryClient);
    }
}

3、测试不同的用户进入系统注册websocket,查看路由信息
查看IM服务提供信息:
在这里插入图片描述

im-10-10-17-126-7009 为0号服务
im-10-10-22-174-7009 为1号服务

测试预计:
530686546882331052 % 2 = 0 应路由至0号服务
530686546882331635 % 2 = 1 应路由至 1号服务

测试结果:
用户 530686546882331052 路由到0号服务:
在这里插入图片描述

用户 530686546882331635 路由到1号服务
在这里插入图片描述

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

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

相关文章

Bonree ONE荣获信通院“2022IT新治理年度明星产品”

今日&#xff0c;由信通院主办的“GOLFIT新治理领导力论坛”正式召开&#xff0c;论坛上公布了2022IT新治理年度评选活动的结果&#xff0c;博睿数据一体化智能可观测平台Bonree ONE凭借卓越的产品力以及优秀的用户体验&#xff0c;从一众产品中脱颖而出&#xff0c;获得“2022…

spring 事务@Transantional 失效及解决方案和总结

1、线程中方法&#xff0c;事务会失效 2、线程中方法&#xff0c;事务会失效。即使在线程方法上增加Transactional注解 3、事务正常回滚&#xff0c;A方法调用B的普通方法 4、事务正常回滚。A方法调用B的private普通方法 6、会抛出NullPointerException异常。 Methods ann…

rock3a: 基于自建数据集+yolov5s模型的rknn模型训练部署全流程

上一篇文章其实已经详述了模型训练到部署的整个流程&#xff0c;但是数据集到模型都是用的官方的coco数据集&#xff0c;这里为了记录开发板的模型训练到部署的整个流程&#xff0c;重新开了一篇文章进行记录。 首先准备数据集和rockchip官方推荐的yolov5源代码 这里需要注意的…

基于Node.js Vue企业产品展示网站

摘 要随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&am…

CAN201-Computer Network(2)

文章目录4. Network Layer4.1 Router4.1.1 Input port functions4.1.2 Destination-based forwarding4.1.3 Switching fabrics4.1.4 Input port queueing4.1.5 Output ports4.2 Internet Protocol4.2.1 IP fragmentation, reassembly4.3 IPv4 addressing4.3.1 Subnets4.3.2 Net…

高等数学(第七版)同济大学 习题11-4 个人解答

高等数学&#xff08;第七版&#xff09;同济大学 习题11-4 函数作图软件&#xff1a;Mathematica 1.设有一分布着质量的曲面Σ&#xff0c;在点(x,y,z)处它的面密度为μ(x,y,z)&#xff0c;用对面积的曲面积分表示这曲面对于x轴的转动惯量.\begin{aligned}&1. \ 设有一分…

IB生物课程介绍与Topic 1: Cell Biology考点分享

准备让孩子就读国际学校或者孩子正在国际学校就读的家长肯定听说过“IB”或者“IB班”&#xff0c;那IB究竟是什么呢&#xff1f;IB与IB课程 IB是International Baccalaureate&#xff08;国际文凭&#xff09;的简称&#xff0c;其课程体系国际文凭大学预科课程&#xff08;In…

Educational Codeforces Round 141 (Rated for Div. 2)A——C

ps&#xff1a;先自我检讨...自从世界杯开始后&#xff0c;就一直摆烂到现在。直到打了今年的第一场cf&#xff0c;看见打的这么菜&#xff0c;真是想remake/。后面我会陆陆续续的补完前段时间没有打的比赛... Dashboard - Educational Codeforces Round 141 (Rated for Div. …

ReentrantLock

目录 ReentrantLock ReentrantLock语法 ReentrantLock可重入 ReentrantLock可打断 ReentrantLock锁超时 ReentrantLock解决哲学家就餐问题 ReentrantLock公平锁 ReentrantLock条件变量 ReentrantLock ReentrantLock 相比于synchronized的特点 : 可中断:比如A线程拥有…

基于移动最小二乘法的曲线曲面拟合论文阅读笔记

基于移动最小二乘法的曲线曲面拟合论文阅读笔记 论文地址:http://www.cnki.com.cn/Article/CJFDTotal-GCTX200401016.htm 一、Problem Statement 传统的曲线(曲面)拟合方法一般使用最小二乘法&#xff0c; 通过使误差的平方和最小&#xff0c; 得到一个线性方程组&#xff0…

通过alist挂在阿里网盘的方法

1、在github官网https://github.com/alist-org/alist/releases/download/v3.8.0/alist-windows-amd64.zip下载alist软件客户端&#xff0c;双击运行&#xff0c;可以看到默认的密码和服务器地址&#xff0c;打开网页http://localhost:5244/manage/accounts&#xff0c;填写密码…

【Linux】Linux工具

文章目录软件包管理器yumVIM编辑器Linux编译器-gcc/g使用gcc如何完成预处理(进行宏替换)编译(生成汇编)汇编(生成机器课识别代码)链接在这里涉及到一个重要的概念: 函数库函数库一般分为静态库和动态库两种gcc选项gcc选项记忆Linux项目自动化构建工具-make/Makefile背景理解实例…

解决vue中报错 Duplicate keys detected:‘1‘. This may cause an update error.

报错截图&#xff1a; 报错原因&#xff1a; 通过上图的报错信息我们不难看出&#xff0c;报错的主要原因出现在 key 值上&#xff0c;报错的意思大概是检测到重复的 key 值&#xff0c;通俗来讲就是你的 key 值不是唯一的。 解决方案&#xff1a; 问题的根源找到了&#xff…

【初阶数据结构】——链表常见面试题剖析

文章目录前言题目1&#xff1a;移除链表元素题目分析思路1&#xff1a;暴力求解思路2&#xff1a;取非val值尾插至新链表思路讲解思考代码实现不带哨兵位带哨兵位题目2&#xff1a;合并两个有序链表题目分析思路讲解代码实现不带哨兵位带哨兵位题目3&#xff1a;反转链表题目分…

用ode45解微分方程遇到的实际问题

最近在用ode45解微分方程数值解&#xff0c;试图复现论文中的图。一般来说说微分方程&#xff08;组&#xff09;只要按照响应的条件去撰写好对应的回调函数即可&#xff0c;基本没什么难度&#xff0c;但对于本文遇到的的这个问题&#xff0c;可能还需要一些技巧去实现解法&am…

动态路由和导航守卫

一、动态路由1、什么是动态路由&#xff1f;将URL地址中可变的内容设置成参数&#xff0c;根据不同的参数渲染不同的组件。&#xff08;组件可以复用&#xff09;2、动态路由如何进行参数的传递&#xff1a;&#xff08;1&#xff09;如何设置URL地址中的参数&#xff1a;’/ur…

【手写 Vue2.x 源码】第十二篇 - 生成 ast 语法树-流程说明

一&#xff0c;前言 上篇&#xff0c;主要介绍了 vue 数据渲染核心流程&#xff0c;涉及以下几个点&#xff1a; 初次渲染时 template 模板被编译为 ast 语法树&#xff1b;通过 ast 语法树生成 render 函数&#xff1b;通过 render 函数返回 vnode 虚拟节点&#xff1b;使用…

基于K8s的DevOps平台实践(三)

文章目录前言1. Jenkins与k8s集成&#x1f351; 插件安装及配置&#x1f351; 演示动态slave pod&#x1f351; Pod-Template中容器镜像的制作&#x1f351; 实践通过Jenkinsfile实现demo项目自动发布到kubenetes环境2. Jenkins集成Sonarqube&#x1f351; sonarqube架构简介&a…

初阶产品经理必看:如何快速进阶B端产品经理

​从去年开始&#xff0c;大批的互联网企业开始转战B端&#xff0c;众多传统企业也早在几年前就开始向互联网转型。 产业互联网的兴起&#xff0c;让一个岗位的潜藏价值正在逐渐爆发&#xff0c;尤其是富有经验的背景&#xff0c;更加让身价越来越高。 这个岗位就是&#xff…

QProcess的非阻塞式用法以及QApplication::processEvents的使用

一、QProcess的阻塞模式QProcess的应用场景非常广泛。可以使用它在qt程序中执行其他进程&#xff0c;并与之进行通信。当使用它执行一些终端命令和操作时&#xff0c;命令和操作往往是需要一定的时间的&#xff0c;这时QProcess本身提供了方法如&#xff1a;waitForStarted() /…