詳解.NET中負載均衡的使用
一、簡介
負載均衡(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")); } }
到此這篇關於詳解.NET中負載均衡的使用的文章就介紹到這瞭,更多相關 .NET負載均衡 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- .Net Core使用SignalR實現鬥地主遊戲
- .Net Api 中使用Elasticsearch存儲文檔的方法
- C#中word導出功能的騷操作詳解
- C# 通過ServiceStack 操作Redis
- Unity實現紅酒識別的示例代碼