.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> publicvoidMoveNextIndex (ref int currentIndex) { if(currentIndex!= requstRing.Length - 1) { currentIndex = currentIndex + 1; } else { currentIndex = 0; } }
測試程式如下:
1public class Program 2{ 3static LimitService l = new LimitService(1000, 1); 4public static void Main(string[] args) 5{ 6 7int threadCount = 50; 8 9while (threadCount >= 0) 10{ 11Thread t = new Thread(s => { 12Limit(); 13 14}); 15 16t.Start(); 17 18threadCount--; 19} 20 21Console.ReadKey(); 22} 23 24public static void Limit() 25{ 26int i = 0; 27int okCount = 0; 28int noCount = 0; 29Stopwatch w = new Stopwatch(); 30w.Start(); 31while (i < 1000000) 32{ 33var ret = l.IsContinue(); 34if (ret) 35{ 36okCount++; 37} 38else 39{ 40noCount++; 41} 42i++; 43} 44w.Stop(); 45Console.WriteLine($"共用{w.ElapsedMilliseconds},允許:{okCount},攔截:{noCount}"); 46} 47}
測試結果:
最大用時7秒,共處理請求1000000*50=50000000 次
並未發生GC操作,記憶體使用率非常低,每秒處理 300萬次+請求 。以上程式修改為10個執行緒,大約用時4秒之內
如果是強勁的伺服器或者執行緒數較少情況下處理速度將會更快!!!
以上就是測試的限制高併發的一種簡單方案,當然還有其他方式比如:令牌桶演算法,漏桶演算法等等,可以去研究下!
以上僅為個人觀點,如果錯誤,請大家指標,謝謝!