SpringBoot使用自定義注解+AOP+Redis實現(xiàn)接口限流的實例代碼
為什么要限流
系統(tǒng)在設(shè)計的時候,我們會有一個系統(tǒng)的預(yù)估容量,長時間超過系統(tǒng)能承受的TPS/QPS閾值,系統(tǒng)有可能會被壓垮,最終導(dǎo)致整個服務(wù)不可用。為了避免這種情況,我們就需要對接口請求進行限流。
所以,我們可以通過對并發(fā)訪問請求進行限速或者一個時間窗口內(nèi)的的請求數(shù)量進行限速來保護系統(tǒng)或避免不必要的資源浪費,一旦達(dá)到限制速率則可以拒絕服務(wù)、排隊或等待。
限流背景
系統(tǒng)有一個獲取手機短信驗證碼的接口,因為是開放接口,所以為了避免用戶不斷的發(fā)送請求獲取驗證碼,防止惡意刷接口的情況發(fā)生,于是用最簡單的計數(shù)器方式做了限流,限制每個IP每分鐘只能請求一次,然后其他每個手機號的時間窗口限制則是通過業(yè)務(wù)邏輯進行判斷。一般一些接口訪問量比較大的,可能會壓垮系統(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); //請求進來,對應(yīng)的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)絡(luò)繁忙,請稍候再試"); } } /** * 獲取限流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è)務(wù)異常 */ @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ù)器更嚴(yán)謹(jǐn))、令牌桶等...,有興趣的小伙伴可以學(xué)習(xí)一下
附源碼
https://gitee.com/jae_1995/ratelimiter
到此這篇關(guān)于SpringBoot使用自定義注解+AOP+Redis實現(xiàn)接口限流的文章就介紹到這了,更多相關(guān)SpringBoot接口限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Security 自定義資源服務(wù)器實踐過程
這篇文章主要介紹了Spring Security 自定義資源服務(wù)器實踐,我們通過自己搭建的授權(quán)服務(wù)器和資源服務(wù)器,完整體驗了OAuth2流程,需要的朋友可以參考下2022-08-08快速搭建Spring Boot+MyBatis的項目IDEA(附源碼下載)
這篇文章主要介紹了快速搭建Spring Boot+MyBatis的項目IDEA(附源碼下載),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12詳解Kotlin 高階函數(shù) 與 Lambda 表達(dá)式
這篇文章主要介紹了詳解Kotlin 高階函數(shù) 與 Lambda 表達(dá)式的相關(guān)資料,需要的朋友可以參考下2017-06-06Spring-Web與Spring-WebFlux沖突問題解決
Spring WebFlux是一套全新的Reactive Web技術(shù)棧,實現(xiàn)完全非阻塞,支持Reactive Streams背壓等特性,這篇文章主要給大家介紹了關(guān)于Spring-Web與Spring-WebFlux沖突問題解決的相關(guān)資料,需要的朋友可以參考下2024-04-04