1)參考了網絡上的算法,可是那個算法僅僅是用於展現「權重輪循」的意圖,在真正的網絡下,由於是並行的,因此不可能單純一個簡單的循環能夠解決問題。算法
2)用lock的話性能顯然有損失。服務器
3)想了一陣,結合CAS和volatile等細粒度的鎖的方式,一個真正能夠用軟件描述SLB帶有權重的算法大概是這個樣子(以下):網絡
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace WBasedRobin { /// <summary> /// 用於計算WeightRobin的數據結構 /// </summary> public class WeightedRobin { private readonly int _weight; private int _count; /// <summary> /// 命中次數(累加) /// </summary> public int ChoosenCount { get { return ++_count; } } /// <summary> /// 權重 /// </summary> public int Weight { get { return _weight; } } /// <summary> /// 輸出當前的權重 /// </summary> public override string ToString() { return "Weight:" + Weight.ToString() + "\tCount:" + _count.ToString(); } /// <summary> /// 初始化每個Server的內部值 /// </summary> public WeightedRobin(int weight, int count = 0) { _weight = weight; _count = count; } } public class WeightRobinRule { private List<WeightedRobin> _servers = null; private volatile int _index = -1; private volatile int _currentWeight = 0; private volatile bool _isServerChanging = false; private volatile int _maxWeight = 0; private volatile int _gcdWeight = 0; private int GetMaxWeight(IEnumerable<WeightedRobin> weights) { return weights.Max(w => w.Weight); } private int GetGCDWeight(int big, int small) { if (big < small) { big ^= small; small ^= big; big ^= small; } if (big % small == 0) { return small; } return GetGCDWeight(small, big % small); } private int GetTotalGCD() { int gcd = GetGCDWeight(_servers[0].Weight, _servers[1].Weight); for (int i = 2; i < _servers.Count; ++i) { gcd = GetGCDWeight(gcd, _servers[i].Weight); } return gcd; } /// <summary> /// 初始化權重服務器,至少2臺服務器。 /// </summary> public WeightRobinRule(int totalServers = 2) { Random r = new Random(); _servers = new List<WeightedRobin>(totalServers); for (int i = 0; i < totalServers; i++) { _servers.Add(new WeightedRobin(r.Next(2, totalServers+1),0)); } _maxWeight = GetMaxWeight(_servers); _gcdWeight = GetTotalGCD(); } public void DoRolling() { int copyIndex = 0; int copyIndexNext = 0; int copycw = 0; //當服務器數量發生變化的時候,鎖住該服務直到完畢。 reloop: while (_isServerChanging) ; for (;;) { //拷貝本地的index,用作同步 copyIndex = _index; //計算輪詢的時候下一個的值 copyIndexNext = (copyIndex + 1) % _servers.Count; //同步做用 copycw = _currentWeight; //假定輪詢後的Next=0,說明完成一輪輪詢,權重減去最大公約數 if (copyIndexNext == 0) { copycw -= _gcdWeight; //若是權重已經扣完,從新從大的開始 if (copycw <= 0) { copycw = _maxWeight; } } //若是copyIndex和_index相同,說明是同一個線程搶到的,那麼直接用本地的替換index進行替換 if (Interlocked.CompareExchange(ref _index, copyIndexNext, copyIndex) == copyIndex) { _currentWeight = copycw; try { //若是輪詢的權重大於等於本地權重,選中它便可。 if (_servers[copyIndexNext].Weight >= copycw) { int t = _servers[copyIndexNext].ChoosenCount; break; } } //若是是Index溢出,那麼說明服務器數量確定發生變化了,因此跳過這次輪詢,等下一輪,不處理。 catch (IndexOutOfRangeException) { goto reloop; } } } } /// <summary> /// 移除指定的服務器 /// </summary> public WeightedRobin RemoveByIndex(int index) { _isServerChanging = true; var removedServer = _servers[index]; _servers.RemoveAt(index); _gcdWeight = GetTotalGCD(); _maxWeight = GetMaxWeight(_servers); _isServerChanging = false; return removedServer; } /// <summary> /// 增長新的服務器 /// </summary> public void AddNewServer(int weight) { _isServerChanging = true; _servers.Add(new WeightedRobin(weight, 0)); _gcdWeight = GetTotalGCD(); _maxWeight = GetMaxWeight(_servers); _isServerChanging = false; } /// <summary> /// 格式化輸出結果 /// </summary> public override string ToString() { StringBuilder sbu = new StringBuilder(10); foreach (WeightedRobin wr in _servers) { sbu.AppendLine(wr.ToString() + Environment.NewLine); } return sbu.ToString(); } } }
調用測試代碼以下:數據結構
using System; using System.Threading; using System.Threading.Tasks; namespace WBasedRobin { class Program { static Random r = new Random(); static void Rounding(WeightRobinRule wr) { wr.DoRolling(); } static void Main(string[] args) { WeightRobinRule wr = new WeightRobinRule(5); Timer t = new Timer((j) => { var removedS = wr.RemoveByIndex(0); Console.WriteLine("移除了服務器:"+removedS); }, null, 2050, Timeout.Infinite); t = new Timer((o) => { wr.AddNewServer(6); Console.WriteLine("新增長服務器了。"); }, null, 3000, Timeout.Infinite); Parallel.For(1, 1001, (num) => { Thread.Sleep(r.Next(100, 500)); Rounding(wr); }); Console.WriteLine(wr); Console.ReadLine(); } } }