均衡加权轮询算法

最近闲来无事打算写一个接口,不过我这个接口要实现的功能也是要通过请求第三方接口实现的,因为第三方接口比较多(要花钱的,不给白嫖),于是就本着不浪费的想法,打算把这些接口全用了。
说好就开干,本来打算直接用最简单的轮询算法的,也就是这次用这个,下次用另一个。后面突然想到第三方接口的额度不一致,有些接口的额度比较多有些比较少,如果按最简单的轮询的话会导致部分第三方接口额度耗尽部分又还有额度,于是就打算使用加权轮询算法。
加权轮询算法其实也就是加了个权重,根据权重来选择对应的接口。说来也简单,只要根据权重把对应的接口放一个数组里依次拿就行,但是这样也会有个问题,也就是有部分请求一直在请求其中一个接口,导致其他接口不会被请求,也就是如果有 3 个接口 a,b,c。权重分别为 4,2,1 的话,那 a 接口先是会被请求 4 次,然后才轮到 b 接口请求 2 次。这样多多少少有点不妥。后面了解到 Nginx 的负载均衡用的是均衡加权轮询算法,也算是完美符合我的要求。
网上查了下资料,均衡加权轮询算法的原理如下:

  • 当前所有节点初始值为 0
  • 所有节点的当前权重值加上设定的权重值
  • 从所有节点中取出当前权重值最大的那个作为命中节点
  • 命中节点的当前权重值减去总权重值作为新的当前权重值
  • 所有节点的当前权重值加上设定权重值作为新的当前权重值

例如节点 A、B、C 三个节点的权重值分别为 4、2、1,其演算步骤如下表:

步骤选择前当前权重值命中节点选择后当前权重
1{ 4, 2, 1 }A{ -3, 2, 1 }
2{ 1, 4, 2 }B{ 1,-3, 2 }
3{ 5,-1, 3 }A{ -2,-1, 3 }
4{ 2, 1, 4 }C{ 2, 1,-3 }
5{ 6, 3,-2 }A{ -1, 3,-2 }
6{ 3, 5,-1 }B{ 3,-2,-1 }
7{ 7, 0, 0 }A{ 0, 0, 0 }

解析:
在步骤一中,当前权重原本为 0:0:0,先加上设定的权重,也就是 4:2:1,变成了 4:2:1,从 4:2:1 中取权重最大的,也就是命中了节点 A,然后节点 A 的权重减去总权重 4+2+1=7,于是变成了-3:2:1。
到步骤二,当前权重为-3:2:1,加上设定的权重 4:2:1,变成了 1:4:2,取出权重最大的节点 B,节点 B 再减去总权重后变成 1:-3:2。
以此类推,最终演算成上表。
由上面的表格可以发现,三个节点的命中符合 4:2:1,而且权重大的节点也不会一直被命中,而是各个节点均衡的被命中,由此循环,一直会保持4:2:1的权重。
原理明白了,接下来就是上代码了。
1、 首先我在配置文件中写入 url 配置:

ys:
  urls:
    - url: aaa
      weight: 4
      currentWeight: 4
    - url: bbb
      weight: 2
      currentWeight: 2
    - url: ccc
      weight: 1
      currentWeight: 1

其中 url 是第三方接口,weight 是设定的权重,currentWeight 是当前权重。
2、编写配置类:

@Data
@Configuration
@ConfigurationProperties(prefix = "ys")
public class YsConfig {

    private List<Urls> urls;

}
@Data
public class Urls {

    private String url;

    private Integer weight;

    private Integer currentWeight;

}

3、接下来就是写方法了

@RequestMapping("/api")
@RestController
public class FileController {

    private final YsConfig ysConfig;

    private List<Urls> urlList;
    private Integer totalWeight;

    public FileController(YsConfig ysConfig) {
        // 初始化第三方接口配置
        this.ysConfig = ysConfig;
        urlList = ysConfig.getUrls();
        // 求出总权重
        totalWeight = urlList.stream().mapToInt(Urls::getWeight).sum();
    }

    @GetMapping("/url")
    public String url() {
        return getUrl().getUrl();
    }

    private Urls getUrl() {
        // 获取当前权重最大的url
        Urls max = urlList.stream().max(Comparator.comparingInt(Urls::getCurrentWeight)).get();
        // 拿到最大权重的url后,将其当前权重减去总权重
        max.setCurrentWeight(max.getCurrentWeight() - totalWeight);
        // 当前权重加上设定权重
        for (Urls url : urlList) {
            url.setCurrentWeight(url.getCurrentWeight() + url.getWeight());
        }
        // 返回最大权重的url
        return max;
    }
}

运行之后我们可以发现其结果是符合表格的数据的。
但是如果直接操作这个公共的urlList 的话,并发量一大就会有问题,我们可以测试一下。

@RequestMapping("/api")
@RestController
public class FileController {


    private final YsConfig ysConfig;

    private List<Urls> urlList;
    private Integer totalWeight;

    // 这里使用CopyOnWriteArrayList,可以保证线程安全,保证结果不出错,并且效率高
    private CopyOnWriteArrayList<Urls> list = new CopyOnWriteArrayList <>();

    public FileController(YsConfig ysConfig) {
        this.ysConfig = ysConfig;
        urlList = ysConfig.getUrls();
        totalWeight = urlList.stream().mapToInt(Urls::getWeight).sum();
    }

    @GetMapping("/url")
    public String url() throws InterruptedException {
        // 先清除list的数据
        list.clear();
        // 使用CountDownLatch计数
        CountDownLatch countDownLatch = new CountDownLatch(10);
        // 启动10个线程,每个线程循环70次,获取url,并将url添加到list中
        for (int i = 0; i < 10; i++) {
            // 这里是使用了java21的虚拟线程,没有的可以直接用线程代替
            Thread.ofVirtual().start(() -> {
                for (int j = 0; j < 70; j++) {
                    Urls url = getUrl();
                    list.add(url);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        int a = 0;
        int b = 0;
        int c = 0;
        System.out.println("总数:" + list.size());
        // 统计每个url的次数
        for (Urls url : list) {
            if (url.getUrl().equals("aaa")) {
                a++;
            }
            if (url.getUrl().equals("bbb")) {
                b++;
            }
            if (url.getUrl().equals("ccc")) {
                c++;
            }
        }
        System.out.println("aaa:" + a);
        System.out.println("bbb:" + b);
        System.out.println("ccc:" + c);

        return "aaa";
    }

    private Urls getUrl() {
        // 获取当前权重最大的url
        Urls max = urlList.stream().max(Comparator.comparingInt(Urls::getCurrentWeight)).get();
        // 拿到最大权重的url后,将其当前权重减去总权重
        max.setCurrentWeight(max.getCurrentWeight() - totalWeight);
        // 当前权重加上设定权重
        for (Urls url : urlList) {
            url.setCurrentWeight(url.getCurrentWeight() + url.getWeight());
        }
        // 返回最大权重的url
        return max;
    }
}

请求之后,我们发现实际结果并没有按照 4:2:1 的权重,这就是高并发下的问题。

总数:700
aaa:400
bbb:195
ccc:105
总数:700
aaa:399
bbb:203
ccc:98

那我们要如何解决呢,我这里是使用了AtomicInteger 这个类来修饰currentWeight
Urls 类中修改currentWeight

@Data
public class Urls {

    private String url;

    private Integer weight;

    private AtomicInteger currentWeight;

    // 这里需要手动写一下set方法,因为数字类型不能直接赋值到AtomicInteger
    public void setCurrentWeight(Integer currentWeight) {
        this.currentWeight = new AtomicInteger(currentWeight);
    }
}

同时修改一下 getUrl 方法

private Urls getUrl() {
    // 这里修改了currentWeight的获取方式
    Urls max = urlList.stream().max(Comparator.comparingInt(x -> x.getCurrentWeight().get())).get();
    // 这里通过addAndGet方法去减掉总权重
    max.getCurrentWeight().addAndGet(-totalWeight);
    // 当前权重加上设定权重
    for (Urls url : urlList) {
        // 这里设置当前权重加上设定权重
        url.getCurrentWeight().addAndGet(url.getWeight());
    }
    // 返回最大权重的url
    return max;
}

重新运行我们会发现每次请求的结果都满足 4:2:1 了。

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

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

相关文章

Levenberg-Marquardt (LM) 算法进行非线性拟合

目录 1. LM算法2. 调包实现3. LM算法实现4. 源码地址 1. LM算法 LM算法是一种非线性最小二乘优化算法&#xff0c;用于求解非线性最小化问题。LM主要用于解决具有误差函数的非线性最小二乘问题&#xff0c;其中误差函数是参数的非线性函数&#xff0c;需要通过调整参数使误差函…

eNSP学习——静态路由及默认路由基本配置

目录 知识背景 实验目的 实验步骤 实验内容 实验拓扑 实验编址 实验前期准备 实验步骤 1、基本配置&#xff08;按照实验编址设置好对应的IP地址&#xff09; 2、是实现主机之间的通信 3、实现全网全通来增强网络的可靠性 4、使用默认路由实现简单的网络优化 需要各…

HTB靶场 Perfection

端口 打开了ssh和http服务 访问 Perfection靶机的网站 是一个根据权重计算总成绩的网站 Wappalyzer查看网页用的什么编写搭建的 抓包看一下是怎么工作的 发送,&#xff0c;返回的结果 如果我在 类别 后面多加一句命令 就会出现提示 恶意输入阻止 大概率有命令注入 通过插件…

解决宏定义后面无法加分号

总结&#xff1a;注意是针对单行if语句使用&#xff0c;并且宏定义后面必须带分号&#xff08;格式统一&#xff09; 参考连接 C语言种do_while(0)的妙用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1vk4y1R7VJ/?spm_id_from333.337.search-card.all.click&vd_…

2万8金句美句格言签名句子ACCESS\EXCEL数据库

优美句子类的数据已经有《33万多优美句子经典句子ACCESS数据库》、《近2万签名的句子网络签名ACCESS数据库》、《24万QQ伤感签名微信签名ACCESS数据库》、《2万多条QQ签名论坛签名大全ACCESS数据库》&#xff0c;今天又遇到一个&#xff0c;感觉也很不错&#xff0c;发上来看看…

pip安装的python包放在哪里了?—— ubuntu系统

1 pip 安装了哪些包 2 包安装在哪里了 thirty-twott:~/Desktop$ pip show openai Name: openai Version: 1.19.0 Summary: The official Python library for the openai API Home-page: Author: Author-email: OpenAI <supportopenai.com> License: Location: /ho…

Cairo

文章目录 关于 Cairo 关于 Cairo 官网&#xff1a;https://cairographics.org官方文档&#xff1a;https://cairographics.org/documentation/ Cairo是一个支持多个输出设备的2D图形库。 当前支持的输出目标 包括 X Window System&#xff08;通过Xlib 和 XCB&#xff09;、Qu…

Gartner发布攻击面管理创新洞察:CTEM、VA、EASM、CAASM、ASA、DRPS、BAS、VM等攻击面管理相关技术及关系

安全运营团队负责管理跨内部和外部数字资产的复杂攻击面。这项研究概述了攻击面评估空间&#xff0c;以帮助安全和风险管理领导者驾驭技术并改善其安全状况。 主要发现 随着本地和云中的技术环境变得越来越复杂和分散&#xff0c;组织必须管理不断增长的攻击面。 SaaS 应用程序…

wordpress 突然报错Error establishing a database connection

wordpress 突然报错Error establishing a database connection 通过在宝塔端多种方式检测测&#xff0c;查看到时Mysql服务挂了&#xff0c;重启Mysql即可

cad中快速计算多个矩形面积的方法

1、输入命令reg&#xff0c;选中矩形创建面域 2、输入命令uni,选中刚刚创建的面域&#xff0c;组合成一个面域 3、输入命令&#xff1a;LI &#xff0c;选中面域&#xff0c;即可查看面积和周长 需注意的一点&#xff0c;开始创建的矩形或者多段线要在一个面内&#xff0c;就是…

OpenCV——Bernsen局部阈值二值化方法

目录 一、Bernsen算法1、算法概述2、参考文献二、代码实现三、结果展示Bernsen局部阈值二值化方法由CSDN点云侠原创,爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、Bernsen算法 1、算法概述 Bernsen 算法是另一种流行的局部阈值二值化方…

美硕科技授权世强硬创代理,继电器具备控制功率小、电磁干扰小特点

受工业自动化、智能制造、物联网以及可再生能源等领域发展的推动&#xff0c;全球继电器市场在过去几年中持续增长&#xff0c;预计未来几年将继续保持这一趋势。 为满足日益增长的市场需求&#xff0c;世强先进&#xff08;深圳&#xff09;科技股份有限公司&#xff08;下称…

摸龙头 交好运 五一就来龙卦山加载好运吧!

龙卦山国风嘉年华又又又来啦&#xff01; 让我看看是谁还没有上车&#xff01;赶紧把五一的行程安排起来&#xff01; 甲辰龙年全新玩法等你来解锁&#xff01; 准备好了吗&#xff0c;接下来跟我一起开启你在龙卦山的一天&#xff01; 五一特种兵的早八时间 8:00AM - 8:00…

Cooper在线协同插件:赋能团队,共筑高效协作新篇章

在数字化浪潮席卷全球的今天&#xff0c;高效协作已成为项目成功的关键要素。为了满足现代团队对协作的迫切需求&#xff0c;我们隆重推出Cooper在线协同插件——一款专为代码项目协作量身打造的精英级工具。凭借卓越的安全性、高效性和易用性&#xff0c;Cooper正迅速成为团队…

代理IP供应商的代理池大小怎么看?

代理池作为网络爬虫、数据采集和隐私保护等领域中的重要工具&#xff0c;扮演着连接真实网络和爬虫之间的桥梁。代理池的大小是影响其性能和可用性的关键因素之一。在这篇文章中&#xff0c;我们将深入探讨代理池的大小对业务的影响&#xff0c;并探讨在不同情况下如何选择合适…

代理IP干货:如何正确使用防范风险?

在今天的数字时代&#xff0c;代理IP地址已成为互联网世界中不可或缺的一部分。无论您是寻求绕过地理限制、保护个人隐私还是执行网络任务&#xff0c;代理IP地址都发挥着关键作用。我们将为您探讨代理IP地址的重要性以及如何防范潜在的风险和威胁。 一、代理IP地址的潜在风险 …

【OceanBase系列】—— 常用运维操作(备忘)

作者简介&#xff1a; 花名&#xff1a;绪宁&#xff0c;OceanBase 数据库解决方案架构师 创建租户 方法一&#xff1a;OCP 创建 确认可分配资源 具体可以分配多少内存&#xff0c;可以通过【资源管理】查看各节点的剩余资源 2. 新建租户 3. 填写租户信息 zone 优先级主要是 p…

WMS系统如何满足多种仓储模式需求

一、WMS系统的基本功能 WMS系统通常具备以下基本功能&#xff1a;入库管理、出库管理、库存管理、订单管理、报表分析等。这些功能能够实现对仓库内货物的实时监控、追踪和查询&#xff0c;确保货物的准确、高效流通。 二、WMS系统如何适应不同的仓储模式 静态仓储模式 静态…

PyCharm开发工具安装plugins插件

一. 简介 通过前面的学习&#xff0c;我们知道 python开发常用的一个开发工具&#xff08;即IDE&#xff09;是 PyCharm。 本文来简单介绍一下&#xff0c;PyCharm开发工具是如何安装 plugins插件的。其实与 vscode软件安装插件类似。 本文来学习 PyCharm开发工具安装一个中…

Github2024-04-25 开源项目日报Top10

根据Github Trendings的统计,今日(2024-04-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目4非开发语言项目2TypeScript项目2PowerShell项目1C++项目1Dart项目1JavaScript项目1GPT4All: 在边缘运行开源大型语言模型 创建周期:…
最新文章