負載均衡算法中的加權隨機算法

import org.apache.commons.lang3.tuple.Pair;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
* 加權隨機,nacos
*/
public class RouterWeightRandom {
/**
*
* @param list [{"a":1,"b":3,"c":6}]
*/
public String route(List<Pair<String,Double>> list) {
List<String> addresses = convert2Addresses(list);
// [0.1,0.4,1]
double[] cumulativeWeights = calcCumulativeWeight(list);
// 0.4/0.3/0.99999
double random = ThreadLocalRandom.current().nextDouble(0, 1);
// 下標(0.4時)或 -(插入點:第一個大于num的元素所在下標[0.3時返回1]/若都
// 小于num,則為arr.length[0.99999時返回3])-1
int index = Arrays.binarySearch(cumulativeWeights, random);
if (index < 0) {
// -2 -4
index = -index - 1;
} else {
// b
return addresses.get(index);
}
// 增強校驗
if (index < cumulativeWeights.length && random < cumulativeWeights[index]) {
return addresses.get(index);
}
return null;
}

? ? /**
* 計算累計權重比例
* @param list [{"a":1,"b":3,"c":6}]
* @return index處元素的值= <=index所有元素的比例之和,如 [0.1,0.4,1]
*/
private double[] calcCumulativeWeight(List<Pair<String, Double>> list) {
// 所有權重值累加 10
double originWeightSum = ?0D;
for (Pair<String,Double> item : list) {
double weight = item.getRight();
originWeightSum += weight;
}
// 每一項權重所占比例 [0.1,0.3,0.6]
double[] weightRatios = new double[list.size()];
int index = 0;
for (Pair<String,Double> item : list) {
double singleWeight = item.getRight();
weightRatios[index++] = singleWeight / originWeightSum;
}
// index處元素的值= <=index所有元素的比例之和 [0.1,0.4,1]
double[] cumulativeWeights = new double[list.size()];
double randomRange = 0D;
for (int i = 0; i < index; i++) {
cumulativeWeights[i] = randomRange + weightRatios[i];
randomRange += weightRatios[i];
}
// 允許的誤差
double doublePrecisionDelta = 0.0001;
if (index == 0 || (Math.abs(cumulativeWeights[index - 1] - 1) < doublePrecisionDelta)) {
return cumulativeWeights;
}
throw new RuntimeException("累加權重計算出錯,因為幾率和不等于1");
}

? ? /**
* 獲取地址列表
*/
private List<String> convert2Addresses(List<Pair<String, Double>> list) {
return list.stream().map(Pair::getLeft).collect(Collectors.toList());
}
}

好的,下面我將逐行詳細解釋你提供的 Java 代碼,包括 RouterWeightRandom 類和 testRouterWeightRandom 測試方法的每一行邏輯、作用、運行效果,幫助你徹底理解 加權隨機(Weighted Random)負載均衡算法 的實現原理。

🌟 一、整體說明
這段代碼實現的是 加權隨機負載均衡算法(Weighted Random Load Balancing),它根據每個服務器地址的“權重”來決定其被選中的概率。權重越高,被選中的概率越大。
? RouterWeightRandom:核心算法類,實現加權隨機選擇。
? RouterTest.testRouterWeightRandom():測試類,模擬地址列表并運行算法。

? 二、測試類 RouterTest 解析
Java
深色版本


public class RouterTest {
定義一個測試類,用于測試 RouterWeightRandom。

Java
深色版本

 ? ?
/**
* 加權隨機
*/
@Test
public void testRouterWeightRandom() {
使用 JUnit 的 @Test 注解標記這是一個測試方法,用于驗證加權隨機算法是否工作正常。

Java
深色版本


RouterWeightRandom router = new RouterWeightRandom();
創建一個 RouterWeightRandom 實例,用于調用其 route() 方法進行路由選擇。

Java
深色版本


List<String> list = mockAddressList();
調用 mockAddressList() 方法,獲取一個模擬的服務器地址列表,內容如下:
深色版本


["http://1.1.1.1", "http://1.1.1.2", "http://1.1.1.3", "http://1.1.1.4", "http://1.1.1.5"]

Java
深色版本


List<Pair<String,Double>> finalList = new ArrayList<>();
創建一個 List<Pair<String, Double>> 類型的列表,用于存儲每個地址及其對應的權重值。
? Pair 來自 Apache Commons Lang3,表示一個鍵值對:address 和 weight。

Java
深色版本

 ? ? ? ?
for
(String address : list) {
double weight = ThreadLocalRandom.current().nextDouble(1000
);
finalList.add(Pair.of(address, weight));
System.out.println("address:" + address +",權重:"
+ weight);
}
遍歷每個地址,為每個地址分配一個 0 到 1000 之間的隨機權重,并打印出來。
運行效果示例輸出:
深色版本


address:http://1.1.1.1,權重:345.67
address:http://1.1.1.2,權重:892.34
...
? 這里權重是隨機生成的,實際項目中權重可能來自配置中心(如 Nacos)或健康檢查結果。

Java
深色版本

 ? ? ? ?
for (int i = 0; i < 10
; i++) {
String route = router.route(finalList);
System.out.println((i + 1) + ".route = "
+ route);
}
循環 10 次,每次調用 router.route(finalList) 執行一次加權隨機選擇,并打印選中的地址。
運行效果示例輸出:
深色版本


1.route = http://1.1.1.2
2.route = http://1.1.1.5
...
? 權重高的地址被選中的概率更高。

Java
深色版本

 ? ?
private List<String> mockAddressList()?
{
List<String> list = new
ArrayList<>();
list.add("http://1.1.1.1"
);
list.add("http://1.1.1.2"
);
list.add("http://1.1.1.3"
);
list.add("http://1.1.1.4"
);
list.add("http://1.1.1.5"
);
return
list;
}
模擬生成 5 個服務器地址,用于測試。

? 三、核心類 RouterWeightRandom 詳細解析
Java
深色版本


import
org.apache.commons.lang3.tuple.Pair;
import
java.util.Arrays;
import
java.util.List;
import
java.util.concurrent.ThreadLocalRandom;
import
java.util.stream.Collectors;

/**
* 加權隨機,nacos
*/
public class RouterWeightRandom {
導入所需類,定義主類 RouterWeightRandom,用于實現加權隨機算法。

🔹 方法一:route() —— 主路由方法
Java
深色版本

 ? ?
public String route(List<Pair<String,Double>> list) {
入口方法,輸入是一個地址與權重的配對列表,返回選中的地址。

Java
深色版本


List<String> addresses = convert2Addresses(list);
調用 convert2Addresses() 方法,提取所有地址,生成一個純地址列表。
例如: ["http://1.1.1.1", "http://1.1.1.2", ...]
? 用于后續通過索引返回最終選中的地址。

Java
深色版本

 ? ? ? ?
double[] cumulativeWeights = calcCumulativeWeight(list);
調用 calcCumulativeWeight() 方法,計算累計權重比例數組。
例如: 如果原始權重是 [1, 3, 6],則:
? 總權重 = 10
? 比例 = [0.1, 0.3, 0.6]
? 累計比例 = [0.1, 0.4, 1.0]
? 這個數組用于“輪盤賭”式隨機選擇。

Java
深色版本

 ? ? ? ?
double random = ThreadLocalRandom.current().nextDouble(0, 1);
生成一個 [0, 1) 區間內的隨機數,作為“命中值”。
例如: random = 0.45
? 這個隨機數將在累計權重數組中查找它“落在哪個區間”。

Java
深色版本

 ? ? ? ?
int index = Arrays.binarySearch(cumulativeWeights, random);
使用 二分查找 在 cumulativeWeights 數組中查找 random 的插入位置。
? 如果 random 正好等于某個元素,返回該元素的索引(≥0)。
? 如果 random 不在數組中,返回 -(插入點) - 1(負數)。
例如:
? cumulativeWeights = [0.1, 0.4, 1.0]
? random = 0.45 → 二分查找返回 -3(因為應插入在索引 2 前)
? random = 0.4 → 返回 1
? random = 0.05 → 返回 -1

Java
深色版本

 ? ? ? ?
if (index < 0
) {
index = -index - 1
;
} else
{
return
addresses.get(index);
}
處理二分查找的結果:
? 如果 index < 0:說明 random 不在數組中,需要轉換為應插入的位置。
? index = -index - 1 就是它應該插入的位置,也就是它“落在”的區間。
? 例如:-3 → 2,說明 random=0.45 落在第 2 個區間(即第三個地址)。
? 如果 index >= 0:說明 random 正好等于某個累計值,直接返回該索引對應的地址。
? 這一步完成了“輪盤賭”選擇:根據隨機數找到對應的地址索引。

Java
深色版本

 ? ? ? ?
if
(index < cumulativeWeights.length && random < cumulativeWeights[index]) {
return
addresses.get(index);
}
增強校驗:確保 index 合法,并且 random 確實小于等于該區間的上限。
? 雖然前面已經通過二分查找確定了位置,但這里再次確認。
? 防止邊界問題或浮點誤差。
? 如果滿足條件,返回 index 對應的地址。

Java
深色版本

 ? ? ? ?
return null;
如果以上條件都不滿足(理論上不應該發生),返回 null。
?? 這是一個兜底邏輯,表示選擇失敗。

🔹 方法二:calcCumulativeWeight() —— 計算累計權重
Java
深色版本

 ? ?
private double[] calcCumulativeWeight(List<Pair<String, Double>> list) {
輸入權重列表,返回累計權重比例數組。

Java
深色版本

 ? ? ? ?
double originWeightSum = 0D
;
for
(Pair<String,Double> item : list) {
double
weight = item.getRight();
originWeightSum += weight;
}
計算所有權重的總和。
例如: [1, 3, 6] → originWeightSum = 10

Java
深色版本

 ? ? ? ?
double[] weightRatios = new double
[list.size()];
int index = 0
;
for
(Pair<String,Double> item : list) {
double
singleWeight = item.getRight();
weightRatios[index++] = singleWeight / originWeightSum;
}
計算每個權重占總權重的比例。
例如: [1/10, 3/10, 6/10] = [0.1, 0.3, 0.6]

Java
深色版本

 ? ? ? ?
double[] cumulativeWeights = new double
[list.size()];
double randomRange = 0D
;
for (int i = 0
; i < index; i++) {
cumulativeWeights[i] = randomRange + weightRatios[i];
randomRange += weightRatios[i];
}
構建累計權重數組:
? cumulativeWeights[0] = 0.1
? cumulativeWeights[1] = 0.1 + 0.3 = 0.4
? cumulativeWeights[2] = 0.4 + 0.6 = 1.0
? 最終得到 [0.1, 0.4, 1.0],表示三個區間的邊界。

Java
深色版本

 ? ? ? ?
double doublePrecisionDelta = 0.0001
;
if (index == 0 || (Math.abs(cumulativeWeights[index - 1] - 1
) < doublePrecisionDelta)) {
return
cumulativeWeights;
}
throw new RuntimeException("累加權重計算出錯,因為幾率和不等于1");
校驗累計權重是否接近 1.0(允許浮點誤差)。
? 如果最后一個元素不是 1.0(在誤差范圍內),拋出異常。
? 防止權重計算錯誤導致概率偏差。

🔹 方法三:convert2Addresses() —— 提取地址
Java
深色版本

 ? ?
private List<String> convert2Addresses(List<Pair<String, Double>> list)?
{
return
list.stream().map(Pair::getLeft).collect(Collectors.toList());
}
使用 Java 8 Stream 流,從 Pair<String, Double> 列表中提取所有 String 地址,返回一個純地址列表。
? 用于后續通過索引返回地址。

? 四、算法核心思想:輪盤賭(Roulette Wheel Selection)
? 權重歸一化:將權重轉換為 [0,1] 區間內的比例。
? 構建累計區間:如 [0.1, 0.4, 1.0] 表示三個區間:
? [0, 0.1) → 地址 A
? [0.1, 0.4) → 地址 B
? [0.4, 1.0] → 地址 C
? 生成隨機數:在 [0,1) 內隨機。
? 查找區間:使用二分查找確定隨機數落在哪個區間,返回對應地址。
? 權重越高,區間越寬,被選中的概率越大。

? 五、總結
組件
作用
Pair<String, Double>
存儲地址和權重
calcCumulativeWeight()
計算累計權重比例數組
convert2Addresses()
提取地址列表
Arrays.binarySearch()
快速查找隨機數落在哪個區間
ThreadLocalRandom
生成隨機數

? 六、運行流程圖(簡化)
深色版本


輸入: [(A,1), (B,3), (C,6)]

計算總權重 = 10

計算比例: [0.1, 0.3, 0.6]

累計比例: [0.1, 0.4, 1.0]

生成 random = 0.45

二分查找 → 落在 [0.4, 1.0] 區間 → 選中 C

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916682.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916682.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916682.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI時代SEO關鍵詞優化策略

內容概要 在人工智能&#xff08;AI&#xff09;技術深度融入數字營銷的背景下&#xff0c;搜索引擎優化&#xff08;SEO&#xff09;的關鍵詞優化策略正經歷一場智能變革&#xff0c;這不僅重塑了傳統研究方式&#xff0c;還為企業帶來了全新的競爭機遇。本文將從AI時代SEO的變…

復矩陣與共軛轉置矩陣乘積及其平方根矩陣

設 是一個 的復數矩陣&#xff0c;其共軛轉置矩陣&#xff08;Hermitian 共軛&#xff09;記為 &#xff08;即 &#xff09;&#xff0c;則矩陣 &#xff08; &#xff09;和 &#xff08; &#xff09;的性質如下文所述。1. Hermitian 性&#xff08;自共軛性&#x…

Vue 框架 學習筆記

作為初學者對于Vue框架的學習筆記 總結了Vue框架的核心知識點&#xff0c;包括&#xff1a;1. 基礎概念&#xff1a;漸進式框架、兩種使用方式、Vue實例創建流程、模板語法和響應式特性。2. 常用指令&#xff1a;詳細介紹了v-html、v-show/v-if、v-for、v-on、v-bind、v-model等…

飛牛系統安裝DataEase自定義Docker包

飛牛系統安裝DataEase自定義Docker包背景構造DataEase Docker包1.在Linux 系統中&#xff08;比如我這里選麒麟V10&#xff09;安裝Docker2.準備打包文件3.執行打包4.驗證打好的包上傳DataEase Docker包1.把本地docker 容器導出1.1查看鏡像列表命令&#xff1a;docker images1.…

可配置的PWM外設模塊

&#x1f527; 可配置的PWM外設模塊 基于FPGA的PWM信號發生器&#xff0c;支持 動態周期與占空比配置&#xff0c;無需外部控制信號&#xff0c;適用于 LED 呼吸燈、舵機控制、電機驅動等場景。 仿真波形 參數修改后會晚一個pwm周期才生效&#x1f4cc; 模塊功能 &#x1f9ee;…

從零到一:我是如何用深度學習打造高性能書籍推薦系統的

作者&#xff1a;笙囧同學 | 發布時間&#xff1a;2025年7月28日 | 閱讀時長&#xff1a;15分鐘 &#x1f3af; 前言&#xff1a;為什么要做這個項目&#xff1f; 大家好&#xff0c;我是笙囧同學&#xff01;最近在學習《機器學習基礎》課程時&#xff0c;被推薦系統的魅力深…

OpenRLHF:面向超大語言模型的高性能RLHF訓練框架

“四模型協同調度破資源壁壘&#xff0c;讓70B模型RLHF訓練觸手可及” OpenRLHF 是由 OpenLLMAI 團隊于2024年推出的開源強化學習人類反饋&#xff08;RLHF&#xff09;框架&#xff0c;旨在解決大語言模型&#xff08;LLM&#xff09;對齊訓練中的多模型協調瓶頸與超大規模擴展…

DMETL安裝流程及簡單使用

目錄 安裝調度器 安裝執行器 安裝管理器 啟動服務 進入web管理端 創建數據源 ?編輯 添加表 添加影子表增量 節點監控 DMETL工程流搭建實踐 創建表/視圖 添加sql腳本 添加數據清洗與轉換模塊 添加排序模塊 創建輸出表 連接各模塊并啟動 查看驗證結果 監控管理 …

如何通過代碼操作文件?

1. 為什么使用文件不使用文件&#xff0c;我們所寫的程序存在電腦內存中&#xff0c;程序結束&#xff0c;內存回收&#xff0c;數據就丟失了。再次運行程序也是看不到上次運行時的數據的&#xff0c;如果想要將數據進行持久化保存&#xff0c;就需要使用文件。2. 文件分類&…

unbuntn 22.04 coreutils文件系統故障

文章目錄核心思路具體操作步驟&#xff08;需借助 Ubuntu Live USB&#xff09;1. 準備 Ubuntu Live USB2. 從 Live USB 啟動并掛載系統分區3. 從安裝包中提取完好的 /bin/dir 文件并替換4. 重啟系統并驗證總結前提說明具體操作步驟&#xff08;分階段執行&#xff09;階段1&am…

若依【(前后端分離版)SpringBoot+Vue3】

文章目錄什么是若依使用若依驗證碼的前端實現&#x1f4cc; 前后端驗證碼流程說明文檔1、前端初始化驗證碼2、前端界面顯示3、后端生成驗證碼接口&#xff08;GET /captchaImage&#xff09;4、用戶提交登錄信息5、后端驗證驗證碼邏輯&#xff08;POST /login&#xff09;6、登…

Ubuntu24安裝MariaDB/MySQL后不知道root密碼如何解決

Ubuntu 24.04 安裝 MariaDB 后 root 密碼未知&#xff1f;解決方案在此在 Ubuntu 24.04 上新安裝 MariaDB 后&#xff0c;許多用戶會發現自己不知道 root 用戶的密碼&#xff0c;甚至在安裝過程中也沒有提示設置密碼。這是因為在較新的 MariaDB 版本中&#xff0c;默認情況下 r…

Cloudflare CDN 中設置地域限制并返回特定界面

文章目錄 什么是CDN 什么是Cloudflare 注冊Cloudflare 賬號,添加域名、修改DNS并激活郵箱 阻止或允許特定國家或地區訪問 常見規則表達式 WAF自定義規則 + 自定義錯誤頁面 使用Workers腳本 什么是CDN CDN 是一種優化網站請求處理的機制。它是在用戶訪問網站 (服務器) 時用戶與…

Ubuntu高頻實用命令大全

Ubuntu系統中高頻實用命令 以下為Ubuntu系統中高頻實用命令的分類整理,涵蓋系統管理、文件操作、網絡配置等場景,每個命令附帶簡要說明: 系統信息與管理 uname -a 顯示系統內核版本、主機名等詳細信息。 lsb_release -a 查看Ubuntu發行版版本信息。 uptime 顯示系統運行時…

關于C#的編程基礎:數據類型與變量全解析

一.基本的數據類型 1.什么是數據類型 在編程語言中&#xff0c;數據類型&#xff08;Data Type&#xff09; 是對變量存儲的 “數據的種類” 的定義&#xff0c;它決定了&#xff1a; 變量可以存儲哪些值&#xff08;例如整數、文本、布爾值&#xff09;。這些值在內存中如何…

深入解析 Spring 獲取 XML 驗證模式的過程

關鍵要點Spring 的 XML 驗證模式&#xff1a;Spring 框架在加載 XML 配置文件時&#xff0c;會根據文件內容判斷使用 DTD&#xff08;文檔類型定義&#xff09;或 XSD&#xff08;XML 模式定義&#xff09;進行驗證。自動檢測機制&#xff1a;Spring 默認使用自動檢測&#xff…

復現《Local GDP Estimates Around the World》論文的完整指南

復現《Local GDP Estimates Around the World》論文的完整指南 1. 引言 1.1 論文概述 《Local GDP Estimates Around the World》是一篇重要的經濟地理學研究論文&#xff0c;作者提出了一種創新的方法來估計全球范圍內次國家層面的GDP數據。這項工作填補了全球經濟發展研究中子…

Sql注入 之sqlmap使用教程

一、安裝sqlmap 瀏覽器訪問SQLmap官網 即可下載工具&#xff1b;需要說明的是&#xff0c;SQLmap運行依賴于python環境&#xff0c;所以在下載使用前務必在電腦及終端上安裝好python環境。 通過網盤分享的文件&#xff1a;sqlmap-master.zip鏈接: https://pan.baidu.com/s/1YZi…

安寶特案例丨戶外通信機房施工革新:AR+作業流技術破解行業難題

在數字化浪潮席卷各行各業的今天&#xff0c;傳統戶外通信機房建設領域正經歷一場靜悄悄的變革。作為信息社會的“神經樞紐”&#xff0c;戶外機房的質量直接關系到通信網絡的穩定性&#xff0c;但長期以來&#xff0c;這一領域卻深受施工標準化不足、質量管控難、驗收追溯復雜…

在 CentOS 中安裝 MySQL 的過程與問題解決方案

MySQL 是一款廣泛使用的開源關系型數據庫管理系統&#xff0c;在 CentOS 系統中安裝 MySQL 是很多開發者和運維人員常做的工作。下面將詳細介紹安裝過程以及可能遇到的問題和解決方案。 一、安裝前的準備工作 在安裝 MySQL 之前&#xff0c;需要做好一些準備工作&#xff0c;…