SpringBoot使用自定義注解+AOP+Redis實現(xiàn)接口限流的實例代碼
為什么要限流
系統(tǒng)在設計的時候,我們會有一個系統(tǒng)的預估容量,長時間超過系統(tǒng)能承受的TPS/QPS閾值,系統(tǒng)有可能會被壓垮,最終導致整個服務不可用。為了避免這種情況,我們就需要對接口請求進行限流。
所以,我們可以通過對并發(fā)訪問請求進行限速或者一個時間窗口內(nèi)的的請求數(shù)量進行限速來保護系統(tǒng)或避免不必要的資源浪費,一旦達到限制速率則可以拒絕服務、排隊或等待。
限流背景
系統(tǒng)有一個獲取手機短信驗證碼的接口,因為是開放接口,所以為了避免用戶不斷的發(fā)送請求獲取驗證碼,防止惡意刷接口的情況發(fā)生,于是用最簡單的計數(shù)器方式做了限流,限制每個IP每分鐘只能請求一次,然后其他每個手機號的時間窗口限制則是通過業(yè)務邏輯進行判斷。一般一些接口訪問量比較大的,可能會壓垮系統(tǒng)的,則需要加入流量限制!如:秒殺等...
實現(xiàn)限流
1、引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> 2、自定義限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
/**
* 限流key
*/
String key() default Constants.RATE_LIMIT_KEY;
/**
* 限流時間,單位秒
*/
int time() default 60;
/**
* 限流次數(shù)
*/
int count() default 100;
/**
* 限流類型
*/
LimitType limitType() default LimitType.DEFAULT;
/**
* 限流后返回的文字
*/
String limitMsg() default "訪問過于頻繁,請稍候再試";
}3、限流切面
@Aspect
@Component
public class RateLimiterAspect {
private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
@Autowired
private RedisUtils redisUtils;
@Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
{
int time = rateLimiter.time();
int count = rateLimiter.count();
long total = 1L;
String combineKey = getCombineKey(rateLimiter, point);
try
{
if(redisUtils.hasKey(combineKey)){
total = redisUtils.incr(combineKey,1); //請求進來,對應的key加1
if(total > count)
throw new ServiceRuntimeException(rateLimiter.limitMsg());
}else{
redisUtils.set(combineKey,1,time); //初始化key
}
}
catch (ServiceRuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new ServiceRuntimeException("網(wǎng)絡繁忙,請稍候再試");
}
}
/**
* 獲取限流key
* @param rateLimiter
* @param point
* @return
*/
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
{
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass();
stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
return stringBuffer.toString();
}
}4、寫一個簡單的接口進行測試
@RestController
public class TestController {
@RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分鐘內(nèi)只能請求一次,請稍后重試")
@GetMapping("/hello")
public ResultMsg hello() {
return ResultMsg.success("Hello World!");
}
}5、全局異常攔截
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 業(yè)務異常
*/
@ExceptionHandler(ServiceRuntimeException.class)
public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request)
{
return ResultMsg.error(e.getMessage());
}
/**
* 系統(tǒng)異常
*/
@ExceptionHandler(Exception.class)
public ResultMsg handleException(Exception e, HttpServletRequest request)
{
return ResultMsg.error("系統(tǒng)異常");
}
}6、接口測試
1)第一次發(fā)送,正常返回結(jié)果

2)一分鐘內(nèi)第二次發(fā)送,返回錯誤,限流提示

好了,大功告成啦
還有其他的限流方式,如滑動窗口限流方式(比計數(shù)器更嚴謹)、令牌桶等...,有興趣的小伙伴可以學習一下
附源碼
https://gitee.com/jae_1995/ratelimiter
到此這篇關于SpringBoot使用自定義注解+AOP+Redis實現(xiàn)接口限流的文章就介紹到這了,更多相關SpringBoot接口限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java封裝公共Result結(jié)果返回類的實現(xiàn)
在使用Java開發(fā)接口請求中,我們需要對請求進行進行統(tǒng)一返回值,這時候我們自己封裝一個統(tǒng)一的Result返回類,本文主要介紹了Java封裝公共Result結(jié)果返回類的實現(xiàn),感興趣的可以了解一下2023-01-01
淺談java實現(xiàn)redis的發(fā)布訂閱(簡單易懂)
本篇文章主要介紹了淺談java實現(xiàn) redis的發(fā)布訂閱(簡單易懂),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
Idea如何使用Fast Request接口調(diào)試
這篇文章主要介紹了Idea如何使用Fast Request接口調(diào)試問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
Spring?Boot?3.4.3?基于?Spring?WebFlux?實現(xiàn)?SSE?功能(代碼示例)
Spring Boot 3.4.3 結(jié)合Spring WebFlux實現(xiàn)SSE 功能,為實時數(shù)據(jù)推送提供了優(yōu)雅的解決方案,通過本文的步驟,你可以快速搭建一個基于事件驅(qū)動的后端服務,滿足實時通知或監(jiān)控等需求,感興趣的朋友一起看看吧2025-04-04
使用Java實現(xiàn)HTTP和HTTPS代理服務詳解
這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)HTTP和HTTPS代理服務,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-04-04
使用FeignClient調(diào)用遠程服務時整合本地的實現(xiàn)方法
這篇文章主要介紹了使用FeignClient調(diào)用遠程服務時整合本地的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03

