網站首頁 編程語言 正文
一、簡介
負載均衡(Load Balance),簡稱 LB,就是將并發的用戶請求通過規則后平衡、分攤到多臺服務器上進行執行,以此達到壓力分攤、數據并行的效果。常見的算法也有許多隨機、輪詢、加權等,今天我們就使用 C# 來實現這幾種算法,并講解在實際項目中的使用。
二、應用場景
負載均衡算法在開發層面,使用場景其實并不多。通常在項目重構、轉型、上線大版本新功能等,為了避免上線出現 Bug 應用功能 100% 的掛掉。可以在程序中使用負載均衡,將部分 HTTP 流量,打入項目中新的功能模塊,然后進行監控,出現問題可以及時進行調整。
這樣 AB 測試的場景,也可以在運維或者網關等其他層面實現流量分配。但現實是大多數公司項目因為一些原因沒有這樣的支持,這時開發就可以在項目中使用代碼進行實現。
三、實際案例
有這樣一個需求,電商系統中,有一個預估運費的微服務(ShippingCharge )。此時上面領導來了需求,預估運費要改版,開發預估了一下改動不小。經過兩周的奮斗 ShippingCharge 需求終于開發測試好了,此時要上線,但是掐指一算,萬一有問題不就死翹翹了,而且還和錢相關。
此時負載均衡算法就派上用場了,我們可以讓 10% 的流量打入這次的改動,可以先進行監控,可以再全部切過來。實際項目中,使用的肯定是權重的,后面隨機、輪詢也簡單進行介紹一下其實現。
假設在改動 ShippingCharge 時,沒有修改舊的功能,是在 controller 下面,對 call business 層換成了這次需求的,這樣我們就可以使用負載均衡,讓 10% 的流量打入新的 business,其余的依然走老的 business。
四、算法實現
這里不會說的太精細,會將核心實現代碼做介紹,實際項目中使用需要自己進行一下結合,舉一反三哈
下面定義了一個 ServiceCenterModel 主要用作承載需要負載均衡的對象信息,可以是 call 下游的 url,也可以是程序內的某一算法標
4.1 隨機
隨機算法的先對來講,較為簡單一些,主要根據 Random 與 ServiceList 的數量結合實現。
如下:
/// <summary>
/// 隨機
/// </summary>
public class RandomAlgorithm
{
/// <summary>
/// Random Function
/// </summary>
private static readonly Random random = new Random();
/// <summary>
/// serviceList
/// </summary>
/// <param name="serviceList">service url set</param>
/// <returns></returns>
public static string Get(List<ServiceCenterModel> serviceList)
{
if (serviceList == null)
return null;
if (serviceList.Count == 1)
return serviceList[0].Service;
// 返回一個小于所指定最大值的非負隨機數
int index = random.Next(serviceList.Count);
string url = serviceList[index].Service;
return url;
}
}
模擬 10 次 http request,可以看到對OldBusiness、NewBusiness進行了隨機的返回
public static void Main(string[] args)
{
// 模擬從配置中心讀取 Service
var serviceList = new List<ServiceCenterModel>()
{
new ServiceCenterModel { Service ="OldBusiness"},
new ServiceCenterModel { Service ="NewBusiness"},
};
// 模擬 Http 請求次數
for (int i = 0; i < 10; i++)
{
Console.WriteLine(RandomAlgorithm.Get(serviceList));
}
}
4.2 輪詢
輪詢的實現思路,將每次讀取 ServiceList 的 Index 放到靜態全局變量中,當到 ServiceList 最后一個時從0開始讀取。
如下:
/// <summary>
/// 輪詢
/// </summary>
public class PollingAlgorithm
{
private static Dictionary<string, int> _serviceDic = new Dictionary<string, int>();
private static SpinLock _spinLock = new SpinLock();
/// <summary>
/// Get URL From Service List
/// </summary>
/// <param name="serviceList">Service URL Set</param>
/// <param name="serviceName">Service Name</param>
/// <returns></returns>
public static string Get(List<ServiceCenterModel> serviceList, string serviceName)
{
if (serviceList == null || string.IsNullOrEmpty(serviceName))
return null;
if (serviceList.Count == 1)
return serviceList[0].Service;
bool locked = false;
_spinLock.Enter(ref locked);//獲取鎖
int index = -1;
if (!_serviceDic.ContainsKey(serviceName)) // Not Exist
_serviceDic.TryAdd(serviceName, index);
else
_serviceDic.TryGetValue(serviceName, out index);
string url = string.Empty;
++index;
if (index > serviceList.Count - 1) //當前索引 > 最新服務最大索引
{
index = 0;
url = serviceList[0].Service;
}
else
{
url = serviceList[index].Service;
}
_serviceDic[serviceName] = index;
if (locked) //釋放鎖
_spinLock.Exit();
return url;
}
}
模擬 10 次 http request,可以看到對OldBusiness、NewBusiness進行了輪詢返回
public static void Main(string[] args)
{
// 模擬從配置中心讀取 Service
var serviceList = new List<ServiceCenterModel>()
{
new ServiceCenterModel { Service ="OldBusiness"},
new ServiceCenterModel { Service ="NewBusiness"},
};
// 模擬 Http 請求次數
for (int i = 0; i < 10; i++)
{
Console.WriteLine(PollingAlgorithm.Get(serviceList, "ShippingChargeBusiness"));
}
}
4.3 權重
權重的實現思路,將配置權重的 Service 按照數量放置在一個集合中,然后按照輪詢的方式進行讀取,需要注意的是這的 weight 只能配置大于 0 的整數。
如下:
/// <summary>
/// 權重
/// </summary>
public class WeightAlgorithm
{
private static ConcurrentDictionary<string, WeightAlgorithmItem> _serviceDic = new ConcurrentDictionary<string, WeightAlgorithmItem>();
private static SpinLock _spinLock = new SpinLock();
public static string Get(List<ServiceCenterModel> serviceList, string serviceName)
{
if (serviceList == null)
return null;
if (serviceList.Count == 1)
return serviceList[0].Service;
bool locked = false;
_spinLock.Enter(ref locked);//獲取鎖
WeightAlgorithmItem weightAlgorithmItem = null;
if (!_serviceDic.ContainsKey(serviceName))
{
weightAlgorithmItem = new WeightAlgorithmItem()
{
Index = -1,
Urls = new List<string>()
};
BuildWeightAlgorithmItem(weightAlgorithmItem, serviceList);
_serviceDic.TryAdd(serviceName, weightAlgorithmItem);
}
else
{
_serviceDic.TryGetValue(serviceName, out weightAlgorithmItem);
weightAlgorithmItem.Urls.Clear();
BuildWeightAlgorithmItem(weightAlgorithmItem, serviceList);
}
string url = string.Empty;
++weightAlgorithmItem.Index;
if (weightAlgorithmItem.Index > weightAlgorithmItem.Urls.Count - 1) //當前索引 > 最新服務最大索引
{
weightAlgorithmItem.Index = 0;
url = serviceList[0].Service;
}
else
{
url = weightAlgorithmItem.Urls[weightAlgorithmItem.Index];
}
_serviceDic[serviceName] = weightAlgorithmItem;
if (locked) //釋放鎖
_spinLock.Exit();
return url;
}
private static void BuildWeightAlgorithmItem(WeightAlgorithmItem weightAlgorithmItem, List<ServiceCenterModel> serviceList)
{
serviceList.ForEach(service => //有幾個權重就加幾個實例
{
for (int i = 0; i < service.Weight; i++)
{
weightAlgorithmItem.Urls.Add(service.Service);
}
});
}
}
public class WeightAlgorithmItem
{
public List<string> Urls { get; set; }
public int Index { get; set; }
}
模擬 10 次 http request,可以看到對 OldBusiness 返回了 9 次,NewBusiness 返回了一次
public static void Main(string[] args)
{
// 模擬從配置中心讀取 Service
var serviceList = new List<ServiceCenterModel>()
{
new ServiceCenterModel { Service ="OldBusiness",Weight = 9 },
new ServiceCenterModel { Service ="NewBusiness",Weight = 1 },
};
// 模擬 Http 請求次數
for (int i = 0; i < 10; i++)
{
Console.WriteLine(WeightAlgorithm.Get(serviceList, "ShippingChargeBusiness"));
}
}
原文鏈接:https://juejin.cn/post/7112268530757664805
相關推薦
- 2022-01-16 ES6中Set(集合)、Map以及class類
- 2022-12-03 pytorch模型保存與加載中的一些問題實戰記錄_python
- 2022-08-01 OpenCV連通域數量統計學習示例_python
- 2023-04-24 Android布局控件View?ViewRootImpl?WindowManagerService關
- 2022-07-15 go語言實現銀行卡號Luhn校驗_Golang
- 2022-09-21 .NET中求復雜類型集合的差集、交集、并集_實用技巧
- 2022-07-20 C語言開發中的常見錯誤詳解_C 語言
- 2023-02-02 Nginx中default_server指令問題詳解_nginx
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支