我們知道當網站的訪問量突然很大的時候肯定會對服務器造成影響,甚至無法訪問,如果是正常的訪問那么很好說明業務量增大可以考慮系統的擴展,但是如果是搜索引擎爬蟲頻繁訪問或是一些惡意訪問,那這時候我們就應該限制這些訪問的訪問次數。redis剛好可以解決這個問題
Redis實現限制訪問頻率
1.實現訪問一
限制每個用戶每分鍾最多只能訪問100個頁面。實現思路:key使用有"rate.limiting:IP",value使用數值,用戶每次訪問將value的值通過INCR命令自增1.如果自增后的值是1同時設置過期時間為1分鍾。這樣用戶每次訪問的時候都讀取該鍵的值,如果超過了100就表明該用戶的訪問頻率超過了限制,需要提示用戶稍后訪問。且該鍵每分鍾會自動被刪除。所以下一分鍾又會重新計算,也就達到了限制訪問頻率的目的。
代碼邏輯:
String key = "rage.limiting:"+ip;
// 判斷key是否存在
int flag = exists(key);// key rate.limiting:192.168.88.60
if(flag == 1){
// key 存在 自增1
int count = incr(key);
if(count > 100){
// 超過限制
log.info("訪問頻率超過了限制,請稍后重試");
return ;
}
}else{
// key 不存在
multi(); // 開啟事務
incr(key); // key不存在自增1 值為1
expire(key,60); // 設置過期時間
exec(); // 提交事務
}
2.實現方式二
實現方式一其實還有個問題,比如如果用戶第一分鍾的訪問了99次,前面58秒訪問了9次,后面1秒訪問了90次,然后用戶后一秒也訪問了99次,而后一分鍾的第一秒訪問了90次,后面的58秒訪問了9次,這樣按照上面的算法是沒有問題的,但是這種極端情況大家還是可以發現問題的。
解決方法:先將上面案例中的100次調整為10次便於在次場景中描述,要精確的保證同一個用戶每分鍾最多訪問10次,需要記錄下來用戶每次訪問的時間。因此對每個用戶我們使用一個List列表類型的鍵來記錄他最近10次訪問的時間,一旦鍵中的元素超過10個,就判斷最早的元素距離現在的時間是否小於1分鍾。如果是表示用戶最近1分鍾訪問次數超過了10次,如果不是就將現在的時間加入到隊列中,同時把最早的元素刪除。
邏輯代碼
String key = "rate.limiting:"+IP;
int listLength = llen(key);
if(listLength < 10){
lpush(key,new());
}else{
long time = lindex(key,-1);
if(now()-time < 60){
log.info("訪問頻率超過了限制,請稍后再試");
}else{
lpush(key,now);
ltrim(key,0,9);
}
}