基于Redis?zSet實(shí)現(xiàn)滑動窗口對短信進(jìn)行防刷限流的問題
前言
主要針對目前線上短信被腳本惡意盜刷的情況,用Redis實(shí)現(xiàn)滑動窗口限流
public void checkCurrentWindowValue(String telNum) {
String windowKey = CommonConstant.getNnSmsWindowKey(telNum);
//獲取當(dāng)前時(shí)間戳
long currentTime = System.currentTimeMillis();
//1小時(shí),默認(rèn)只能發(fā)5次,參數(shù)smsWindowMax做成可配置項(xiàng),配置到Nacos配置中心,可以動態(tài)調(diào)整
if (RedisUtil.hasKey(windowKey)) {
//參數(shù)smsWindowTime表示限制的窗口時(shí)間
//這里獲取當(dāng)前時(shí)間與限制窗口時(shí)間之間的短信發(fā)送次數(shù)
Optional<Long> optional = Optional.ofNullable(RedisUtil.zCount(windowKey, currentTime - smsWindowTime, currentTime));
if (optional.isPresent()) {
long count = optional.get();
if (count >= smsWindowMax) {
log.error("==========>當(dāng)前號碼:{} 短信發(fā)送太頻繁,{}", telNum, count);
throw new ServiceException(MidRetCode.umid_10060);
}
}
}
StringBuilder sb =new StringBuilder();
String windowEle = sb.append(telNum).append(":").append(currentTime).toString();
//添加當(dāng)前發(fā)送元素到zSet中(由于保證元素唯一,這里將元素加上了當(dāng)前時(shí)間戳)
RedisUtil.zAdd(windowKey, windowEle, currentTime);
//設(shè)置2倍窗口Key:windowKey 的過期時(shí)間
RedisUtil.expire(windowKey, smsWindowTime*2, TimeUnit.MILLISECONDS);
}補(bǔ)充:下面看下以php語言為例基于redis實(shí)現(xiàn)滑動窗口式的短信發(fā)送接口限流
滑動窗口短信發(fā)送限流算法
1.有兩條規(guī)則
基于IP的限制和基于手機(jī)號的限制
IP規(guī)則:
1分鐘限制5
10分鐘限制30
1小時(shí)限制50
手機(jī)號規(guī)則:
1分鐘限制1
10分鐘限制5
1小時(shí)限制10
2.滑動窗口就是隨著時(shí)間的流動 , 進(jìn)行動態(tài)的刪減區(qū)間內(nèi)的數(shù)據(jù) , 限制時(shí)獲取區(qū)間內(nèi)的數(shù)據(jù)
最主要的是用到了redis的zRemRangeByScore來進(jìn)行刪除區(qū)間外的數(shù)據(jù)
<?php
/*滑動窗口短信發(fā)送限流算法
1.有兩條規(guī)則
基于IP的限制和基于手機(jī)號的限制
IP規(guī)則:
1分鐘限制5
10分鐘限制30
1小時(shí)限制50
手機(jī)號規(guī)則:
1分鐘限制1
10分鐘限制5
1小時(shí)限制10
*/
//IP規(guī)則
$ipRules=array(
60=>5,
600=>30,
3600=>50
);
//手機(jī)號規(guī)則
$phoneRules=array(
60=>1,
600=>5,
3600=>10
);
$r = checkLimits($ipRules,$_SERVER["REMOTE_ADDR"],$_GET['tel']);
var_dump($r);
$r = checkLimits($phoneRules,$_GET['tel'],$_GET['tel']);
var_dump($r);
function checkLimits($rules,$key,$tel){
$redis = new Redis();
$redis->connect('115.159.28.111', 1991);
foreach($rules as $ruleTime=>$rule) {
$redisKey=$key."_".$ruleTime;
$score=time();
$member=$tel.'_'.$score;
$redis->multi();
$redis->zRemRangeByScore($redisKey, 0, $score - $ruleTime);//移除窗口以外的數(shù)據(jù)
$redis->zAdd($redisKey, $score, $member);
$redis->expire($redisKey, $ruleTime);
$redis->zRange($redisKey, 0, -1, true);
$members = $redis->exec();
if (empty($members[3])) {
break;
}
$nums=count($members[3]);
var_dump($nums);
if($nums>$rule){
return false;
}
}
return true;
}到此這篇關(guān)于基于Redis zSet實(shí)現(xiàn)滑動窗口對短信進(jìn)行防刷限流的文章就介紹到這了,更多相關(guān)Redis zSet滑動窗口限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis分布式鎖的使用和實(shí)現(xiàn)原理詳解
這篇文章主要給大家介紹了關(guān)于Redis分布式鎖的使用和實(shí)現(xiàn)原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11

