帶有權重的服務器SLB的實現

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();
        }
    }
}
相關文章
相關標籤/搜索