前言:高併發對咱們來講應該都不陌生,特別想淘寶秒殺,競價等等,使用的很是多,如何在高併發的狀況下,使用限流,保證業務的進行呢。如下是一個實例,不喜勿噴!算法
整體思路:數組
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秒以內
若是是強勁的服務器或者線程數較少狀況下處理速度將會更快!!!
以上就是測試的限制高併發的一種簡單方案,固然還有其餘方式好比:令牌桶算法,漏桶算法等等,能夠去研究下!
以上僅爲我的觀點,若是錯誤,請你們指針,謝謝!