詳解Redis如何優(yōu)雅地實(shí)現(xiàn)接口防刷
背景
最近在學(xué)習(xí)redis,想到了之前的寫的一個(gè)案例demo,實(shí)現(xiàn)了接口的流量防刷。主要是為了防止爬蟲爬取接口,當(dāng)然可以適用于那些需要進(jìn)行流控的系統(tǒng),shigen畫了一張草圖展示主要的原理和過(guò)程:

首先用戶請(qǐng)求系統(tǒng)的接口,后端會(huì)有一個(gè)流控的攔截器,這里邊主要是去獲得請(qǐng)求的信息,用ip+URI作為redis的key,查詢?cè)趓edis中記錄的次數(shù),如果次數(shù)大于規(guī)定的單位時(shí)間的訪問次數(shù),就認(rèn)為這是非法的請(qǐng)求了,就需要攔截了。那具體的代碼該怎么設(shè)計(jì)呢》?下邊是一份詳細(xì)的代碼案例。亮點(diǎn)在于:shigen使用了一個(gè)注解,支持接口的個(gè)性化設(shè)置。
代碼實(shí)現(xiàn)
自定義ReteLimit注解
package main.java.com.shigen.redis.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy,
import java.lang.annotation.Target;
/**
* @author shigenfu
* @date 2023/9/6 10:05 上午
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimit {
/**
* 限定時(shí)間
*
* @return 限定時(shí)間
*/
int seconds() default 1;
/**
* 最大請(qǐng)求數(shù)
*
* @return 最大請(qǐng)求數(shù)!
*/
int max() default 1;
}其實(shí)就是兩個(gè)核心參數(shù)seconds max 規(guī)定了單位時(shí)間內(nèi)的最大訪問次數(shù)。
攔截器rateLimitIntercept
/**
* @author shigenfu
* @date 2023/9/6 10:09 上午
*/
@Component
public class RateLimitIntercept extends HandlerInterceptorAdapter {
@Resource
private RedisTemplate<String, String> redisTemplate;
public static final String RATE_LIMIT_KEY = "rl:",
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws IOException{
// 請(qǐng)求是否是方法的請(qǐng)求
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod =(HandlerMethod) handler:
// 獲得注解
RateLimit methodAnnotation = handlerMethod.getMethodAnnotation(RateLimit.class);
if (methodAnnotation == null) {
return true;
}
int seconds = methodAnnotation.seconds();
int max = methodAnnotation.max();
String key = RATE_LIMIT_KEY + request.getRemoteAddr()+ ":"+ request.getRequestURI();
String times = redisTemplate.opsForValue().get(key);
if (times = null) {
redisTemplate.opsForValue().set(key, String.value0f(1), seconds, TimeUnit.SECONDS);
}else{
int timesInt = Integer.parseInt(times);
if(timesInt < max){
redisTemplate.opsForValue().set(key, String.value0f(timesInt + 1), seconds, TimeUnit.SECONDs);
}else{
response.sendError(429,"to many requests");
return false;
}
}
}
return true,
}
}注冊(cè)到攔截器上
/**
* @author shigenfu
* @date 2023/9/6 11:30 上午
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private RateLimitIntercept rateLimitIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(rateLimitIntercept),
}
}這一步很重要,不然我們自定義的攔截器會(huì)不生效。
測(cè)試類測(cè)試
/**
* @author shigenfu
* @date 2023/9/6 11:25 上午
*/
@RestController
@RequestMapping(value ="rate")
public class RateLimitTestController {
@GetMapping(value = "test")
@RateLimit(seconds=10,max=2)
public String test(){
return UUID.randomUUID().toString();
}
}這里主要是寫了一個(gè)接口實(shí)現(xiàn)測(cè)試,訪問的時(shí)候返回隨機(jī)生成的UUID字符串。為了測(cè)試的效果明顯,我在@RateLimit上配置的是10s之內(nèi)只能允許2次請(qǐng)求。那現(xiàn)在開始測(cè)試吧!
測(cè)試
依舊用到的是ab這個(gè)工具。
ab -c 2 -n 10 127.0.0.1:9000/rate/test
一起看看效果吧。

可以看到測(cè)試的報(bào)告,10個(gè)請(qǐng)求,8個(gè)失敗,2個(gè)成功,數(shù)據(jù)庫(kù)也存儲(chǔ)了對(duì)應(yīng)的key和value,很符合預(yù)期的效果。
頁(yè)面的錯(cuò)誤是這樣的,也很符合預(yù)期。

到此這篇關(guān)于詳解Redis如何優(yōu)雅地實(shí)現(xiàn)接口防刷的文章就介紹到這了,更多相關(guān)Redis接口防刷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis數(shù)據(jù)結(jié)構(gòu)ZipList,QuickList,SkipList使用及說(shuō)明
文章介紹了ZipList數(shù)據(jù)結(jié)構(gòu)及其在Redis中的應(yīng)用,詳細(xì)解釋了其緊湊的內(nèi)存存儲(chǔ)方式和特點(diǎn)和應(yīng)用場(chǎng)景,同時(shí)探討了QuickList及其在Redis中的作用,以及不同數(shù)據(jù)類型的編碼方式和應(yīng)用場(chǎng)景2026-04-04
Redis使用ZSET實(shí)現(xiàn)消息隊(duì)列使用小結(jié)
這篇文章主要介紹了Redis使用ZSET實(shí)現(xiàn)消息隊(duì)列使用總結(jié),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03

