.NET-高並發及限流方案


前言:高並發對我們來說應該都不陌生,特別想淘寶秒殺,競價等等,使用的非常多,如何在高並發的情況下,使用限流,保證業務的進行呢。以下是一個實例,不喜勿噴!

總體思路:

1.  用一個環形來代表通過的請求容器。

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

3.  如果請求通過,則當前指針向前移動一個位置,不通過則不移動位置

4.  重復以上步驟 直到永遠.......

 

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

實例使用.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;
            }
        }

  

 

測試程序如下:

 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秒之內

 

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

以上就是測試的限制高並發的一種簡單方案,當然還有其他方式比如:令牌桶算法,漏桶算法等等,可以去研究下!

以上僅為個人觀點,如果錯誤,請大家指針,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM