.NET-高併發及限流方案

前言:高併發對咱們來講應該都不陌生,特別想淘寶秒殺,競價等等,使用的很是多,如何在高併發的狀況下,使用限流,保證業務的進行呢。如下是一個實例,不喜勿噴!算法

整體思路:數組

1.  用一個環形來表明經過的請求容器。服務器

2.  用一個指針指向當前請求所到的位置索引,來判斷當前請求時間和當前位置上次請求的時間差,依此來判斷是否被限制。併發

3.  若是請求經過,則當前指針向前移動一個位置,不經過則不移動位置高併發

4.  重複以上步驟 直到永遠.......測試

 

如下代碼的核心思路是這樣的:指針當前位置的時間元素和當前時間的差來決定是否容許這次請求,這樣經過的請求在時間上表現的比較平滑。spa

實例使用.net寫的,僅供參考,瞭解思路和原理,須要者徹底能夠用其餘方式語言來實現,很簡單:.net

 public class LimitService
    {
        /// <summary>
        /// 當前指針位置
        /// </summary>
        public int currentIndex = 0;

        //限制的時間的秒數,即:x秒容許多少請求
        public int limitTimeSencond = 1;

        /// <summary>
        /// 請求環的數組容器
        /// </summary>
        public DateTime?[] requstRing { get; set; } = null;

        /// <summary>
        /// 容器改變或者移動指針時的鎖;
        /// </summary>
        object obj = new object();

        public LimitService(int countPerSecond, int _limitTimeSencond)
        {
            limitTimeSencond = _limitTimeSencond;
            requstRing = new DateTime?[countPerSecond];
        }

        /// <summary>
        /// 程序是否能夠繼續
        /// </summary>
        /// <returns></returns>
        public bool IsContinue()
        {
            lock (obj)
            {
                var currentNode = requstRing[currentIndex];
                if (currentNode != null && currentNode.Value.AddSeconds(limitTimeSencond) > DateTime.Now)
                {
                    return false;
                }

                //當前節點設置爲當前時間
                requstRing[currentIndex] = DateTime.Now;
                //指針移動一個位置
                MoveNextIndex(ref currentIndex);
            }

            return true;
        }

        /// <summary>
        /// 改變每秒能夠經過的請求數
        /// </summary>
        /// <param name="countPerSecond"></param>
        /// <returns></returns>
        public bool ChangeCountPerSecond(int countPerSecond)
        {
            lock (obj)
            {
                requstRing = new DateTime?[countPerSecond];

                currentIndex = 0;
            }

            return true;
        }

        /// <summary>
        /// 指針往前移動一個位置
        /// </summary>
        /// <param name="currentIndex"></param>
        public  void  MoveNextIndex (ref int currentIndex)
        {
            if(currentIndex!= requstRing.Length - 1)
            {
                currentIndex = currentIndex + 1;
            }
            else
            {
                currentIndex = 0;
            }
        }

  

 

測試程序以下:pwa

 1  public class Program
 2     {
 3         static LimitService l = new LimitService(1000, 1);
 4         public static void Main(string[] args)
 5         {
 6 
 7             int threadCount = 50;
 8 
 9             while (threadCount >= 0)
10             {
11                 Thread t = new Thread(s => {
12                     Limit();
13 
14                 });
15 
16                 t.Start();
17 
18                 threadCount--;
19             }
20 
21             Console.ReadKey();
22         }
23 
24         public static void Limit()
25         {
26             int i = 0;
27             int okCount = 0;
28             int noCount = 0;
29             Stopwatch w = new Stopwatch();
30             w.Start();
31             while (i < 1000000)
32             {
33                 var ret = l.IsContinue();
34                 if (ret)
35                 {
36                     okCount++;
37                 }
38                 else
39                 {
40                     noCount++;
41                 }
42                 i++;
43             }
44             w.Stop();
45             Console.WriteLine($"共用{w.ElapsedMilliseconds},容許:{okCount},  攔截:{noCount}");
46         }
47     }

 

測試結果:線程

最大用時7秒,共處理請求1000000*50=50000000 次

並未發生GC操做,內存使用率很是低,每秒處理 300萬次+請求 。以上程序修改成10個線程,大約用時4秒以內

 

 若是是強勁的服務器或者線程數較少狀況下處理速度將會更快!!!

以上就是測試的限制高併發的一種簡單方案,固然還有其餘方式好比:令牌桶算法,漏桶算法等等,能夠去研究下!

以上僅爲我的觀點,若是錯誤,請你們指針,謝謝!

相關文章
相關標籤/搜索