詳解.NET中負(fù)載均衡的使用
一、簡(jiǎn)介
負(fù)載均衡(Load Balance),簡(jiǎn)稱 LB,就是將并發(fā)的用戶請(qǐng)求通過(guò)規(guī)則后平衡、分?jǐn)偟蕉嗯_(tái)服務(wù)器上進(jìn)行執(zhí)行,以此達(dá)到壓力分?jǐn)?、?shù)據(jù)并行的效果。常見的算法也有許多隨機(jī)、輪詢、加權(quán)等,今天我們就使用 C# 來(lái)實(shí)現(xiàn)這幾種算法,并講解在實(shí)際項(xiàng)目中的使用。
二、應(yīng)用場(chǎng)景
負(fù)載均衡算法在開發(fā)層面,使用場(chǎng)景其實(shí)并不多。通常在項(xiàng)目重構(gòu)、轉(zhuǎn)型、上線大版本新功能等,為了避免上線出現(xiàn) Bug 應(yīng)用功能 100% 的掛掉??梢栽诔绦蛑惺褂秘?fù)載均衡,將部分 HTTP 流量,打入項(xiàng)目中新的功能模塊,然后進(jìn)行監(jiān)控,出現(xiàn)問(wèn)題可以及時(shí)進(jìn)行調(diào)整。
這樣 AB 測(cè)試的場(chǎng)景,也可以在運(yùn)維或者網(wǎng)關(guān)等其他層面實(shí)現(xiàn)流量分配。但現(xiàn)實(shí)是大多數(shù)公司項(xiàng)目因?yàn)橐恍┰驔]有這樣的支持,這時(shí)開發(fā)就可以在項(xiàng)目中使用代碼進(jìn)行實(shí)現(xiàn)。
三、實(shí)際案例
有這樣一個(gè)需求,電商系統(tǒng)中,有一個(gè)預(yù)估運(yùn)費(fèi)的微服務(wù)(ShippingCharge )。此時(shí)上面領(lǐng)導(dǎo)來(lái)了需求,預(yù)估運(yùn)費(fèi)要改版,開發(fā)預(yù)估了一下改動(dòng)不小。經(jīng)過(guò)兩周的奮斗 ShippingCharge 需求終于開發(fā)測(cè)試好了,此時(shí)要上線,但是掐指一算,萬(wàn)一有問(wèn)題不就死翹翹了,而且還和錢相關(guān)。
此時(shí)負(fù)載均衡算法就派上用場(chǎng)了,我們可以讓 10% 的流量打入這次的改動(dòng),可以先進(jìn)行監(jiān)控,可以再全部切過(guò)來(lái)。實(shí)際項(xiàng)目中,使用的肯定是權(quán)重的,后面隨機(jī)、輪詢也簡(jiǎn)單進(jìn)行介紹一下其實(shí)現(xiàn)。
假設(shè)在改動(dòng) ShippingCharge 時(shí),沒有修改舊的功能,是在 controller 下面,對(duì) call business 層換成了這次需求的,這樣我們就可以使用負(fù)載均衡,讓 10% 的流量打入新的 business,其余的依然走老的 business。
四、算法實(shí)現(xiàn)
這里不會(huì)說(shuō)的太精細(xì),會(huì)將核心實(shí)現(xiàn)代碼做介紹,實(shí)際項(xiàng)目中使用需要自己進(jìn)行一下結(jié)合,舉一反三哈
下面定義了一個(gè) ServiceCenterModel 主要用作承載需要負(fù)載均衡的對(duì)象信息,可以是 call 下游的 url,也可以是程序內(nèi)的某一算法標(biāo)
4.1 隨機(jī)
隨機(jī)算法的先對(duì)來(lái)講,較為簡(jiǎn)單一些,主要根據(jù) Random 與 ServiceList 的數(shù)量結(jié)合實(shí)現(xiàn)。
如下:
/// <summary> /// 隨機(jī) /// </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; // 返回一個(gè)小于所指定最大值的非負(fù)隨機(jī)數(shù) int index = random.Next(serviceList.Count); string url = serviceList[index].Service; return url; } }
模擬 10 次 http request,可以看到對(duì)OldBusiness、NewBusiness進(jìn)行了隨機(jī)的返回
public static void Main(string[] args) { // 模擬從配置中心讀取 Service var serviceList = new List<ServiceCenterModel>() { new ServiceCenterModel { Service ="OldBusiness"}, new ServiceCenterModel { Service ="NewBusiness"}, }; // 模擬 Http 請(qǐng)求次數(shù) for (int i = 0; i < 10; i++) { Console.WriteLine(RandomAlgorithm.Get(serviceList)); } }
4.2 輪詢
輪詢的實(shí)現(xiàn)思路,將每次讀取 ServiceList 的 Index 放到靜態(tài)全局變量中,當(dāng)?shù)?ServiceList 最后一個(gè)時(shí)從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) //當(dāng)前索引 > 最新服務(wù)最大索引 { index = 0; url = serviceList[0].Service; } else { url = serviceList[index].Service; } _serviceDic[serviceName] = index; if (locked) //釋放鎖 _spinLock.Exit(); return url; } }
模擬 10 次 http request,可以看到對(duì)OldBusiness、NewBusiness進(jìn)行了輪詢返回
public static void Main(string[] args) { // 模擬從配置中心讀取 Service var serviceList = new List<ServiceCenterModel>() { new ServiceCenterModel { Service ="OldBusiness"}, new ServiceCenterModel { Service ="NewBusiness"}, }; // 模擬 Http 請(qǐng)求次數(shù) for (int i = 0; i < 10; i++) { Console.WriteLine(PollingAlgorithm.Get(serviceList, "ShippingChargeBusiness")); } }
4.3 權(quán)重
權(quán)重的實(shí)現(xiàn)思路,將配置權(quán)重的 Service 按照數(shù)量放置在一個(gè)集合中,然后按照輪詢的方式進(jìn)行讀取,需要注意的是這的 weight 只能配置大于 0 的整數(shù)。
如下:
/// <summary> /// 權(quán)重 /// </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) //當(dāng)前索引 > 最新服務(wù)最大索引 { 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 => //有幾個(gè)權(quán)重就加幾個(gè)實(shí)例 { 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,可以看到對(duì) 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 請(qǐng)求次數(shù) for (int i = 0; i < 10; i++) { Console.WriteLine(WeightAlgorithm.Get(serviceList, "ShippingChargeBusiness")); } }
到此這篇關(guān)于詳解.NET中負(fù)載均衡的使用的文章就介紹到這了,更多相關(guān) .NET負(fù)載均衡 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
win8/8.1系統(tǒng)安裝.net framework 3.5出現(xiàn)0x800F0906代碼錯(cuò)誤的解決方法
這篇文章主要為大家詳細(xì)介紹了win8/8.1系統(tǒng)安裝.net framework 3.5出現(xiàn)0x800F0906代碼錯(cuò)誤的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Asp.net MVC中使用JQuery插件ajaxFileUpload上傳文件
這篇文章主要介紹了Asp.net MVC中使用JQuery插件ajaxFileUpload上傳文件,需要的朋友可以參考下2016-08-08ASP.NET從字符串中查找字符出現(xiàn)次數(shù)的具體實(shí)現(xiàn)方法
今天在一場(chǎng)“特殊的討論”中引入了一個(gè)問(wèn)題,如何在C#求出字符串中某字符的出現(xiàn)次數(shù),比如求“ADSFGEHERGASDF”中“A”出現(xiàn)的次數(shù)2013-11-11CheckBoxList兩列并排編譯為表格顯示具體實(shí)現(xiàn)
CheckBoxList兩列并排的顯示效果相比大家都有見到過(guò)吧,下面是具體的實(shí)現(xiàn)代碼,感興趣的朋友可以參考下哈2013-05-05.Net Core WebApi部署到Windows服務(wù)器上的步驟
這篇文章主要介紹了.Net Core WebApi部署到Windows服務(wù)器上的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Jenkins編譯.NET?Core、.NET?Framework項(xiàng)目并遠(yuǎn)程部署到IIS
這篇文章介紹了Jenkins編譯.NET?Core、.NET?Framework項(xiàng)目并遠(yuǎn)程部署到IIS的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04ASP.NET(C#) Web Api通過(guò)文件流下載文件的實(shí)例
這篇文章主要介紹了ASP.NET(C#) Web Api通過(guò)文件流下載文件的方法,提供源碼下載,需要的朋友可以參考下。2016-06-06