菜菜哥,有時間嗎?
YY妹,什麼事?
我最近的任務是做個小的秒殺活動,我怕把後端介面壓垮,X總說這可關係到公司的存亡
簡單呀,你就做個限流唄
這個沒做過呀,菜菜哥,幫妹子寫一個唄,事成了,以後有什麼要求隨便說
那好呀,先把我工資漲一下
那算了,我找別人幫忙吧
跟你開玩笑呢,給哥2個小時時間
謝謝菜菜哥,以後你什麼要求我都答應你
好嘞,年輕人就是豪爽
就今天來說的限流,書面意思和作用一致,就是為了限制,透過對併發訪問或者請求進行限速或者一個時間視窗內的請求進行限速來保護系統。一旦達到了限制的臨界點,可以用拒絕服務、排隊、或者等待的方式來保護現有系統,不至於發生雪崩現象。
限流就像做帝都的地鐵一般,如果你住在西二旗或者天通苑也許會體會的更深刻一些。我更習慣在技術角度用消費者的角度來闡述,需要限流的一般原因是消費者能力有限,目的為了避免超過消費者能力而出現系統故障。當然也有其他類似的情況也可以用限流來解決。
限流的表現形式上大部分可以分為兩大類:
1. 限制消費者數量。也可以說消費的最大能力值。比如:資料庫的連線池是側重的是總的連線數。還有菜菜以前寫的執行緒池,本質上也是限制了消費者的最大消費能力。
2. 可以被消費的請求數量。這裡的數量可以是瞬時併發數,也可以是一段時間內的總併發數。菜菜今天要幫YY妹子做的也是這個。
除此之外,限流還有別的表現形式,例如按照網路流量來限流,按照cpu使用率來限流等。按照限流的範圍又可以分為分散式限流,應用限流,介面限流等。無論怎麼變化,限流都可以用以下圖來表示:
2. 用一個指標指向當前請求所到的位置索引,來判斷當前請求時間和當前位置上次請求的時間差,依此來判斷是否被限制。
3. 如果請求透過,則當前指標向前移動一個位置,不透過則不移動位置
4. 重覆以上步驟 直到永遠…….
//限流元件,採用陣列做為一個環
class LimitService
{
//當前指標的位置
int currentIndex = 0;
//限制的時間的秒數,即:x秒允許多少請求
int limitTimeSencond = 1;
//請求環的容器陣列
DateTime?[] requestRing = null;
//容器改變或者移動指標時候的鎖
object objLock = new object();
public LimitService(int countPerSecond,int _limitTimeSencond)
{
requestRing = new DateTime?[countPerSecond];
limitTimeSencond= _limitTimeSencond;
}
//程式是否可以繼續
public bool IsContinue()
{
lock (objLock)
{
var currentNode = requestRing[currentIndex];
//如果當前節點的值加上設定的秒 超過當前時間,說明超過限制
if (currentNode != null&& currentNode.Value.AddSeconds(limitTimeSencond) >DateTime.Now)
{
return false;
}
//當前節點設定為當前時間
requestRing[currentIndex] = DateTime.Now;
//指標移動一個位置
MoveNextIndex(ref currentIndex);
}
return true;
}
//改變每秒可以透過的請求數
public bool ChangeCountPerSecond(int countPerSecond)
{
lock (objLock)
{
requestRing = new DateTime?[countPerSecond];
currentIndex = 0;
}
return true;
}
//指標往前移動一個位置
private void MoveNextIndex(ref int currentIndex)
{
if (currentIndex != requestRing.Length - 1)
{
currentIndex = currentIndex + 1;
}
else
{
currentIndex = 0;
}
}
}
static LimitService l = new LimitService(1000, 1);
static void Main(string[] args)
{
int threadCount = 50;
while (threadCount >= 0)
{
Thread t = new Thread(s =>
{
Limit();
});
t.Start();
threadCount--;
}
Console.Read();
}
static void Limit()
{
int i = 0;
int okCount = 0;
int noCount = 0;
Stopwatch w = new Stopwatch();
w.Start();
while (i 1000000)
{
var ret = l.IsContinue();
if (ret)
{
okCount++;
}
else
{
noCount++;
}
i++;
}
w.Stop();
Console.WriteLine($"共用{w.ElapsedMilliseconds},允許:{okCount}, 攔截:{noCount}");
}