SpringBoot中間件封裝限流器的方案詳解
背景
通常能知道一個(gè)系統(tǒng)服務(wù)在正產(chǎn)增速下流量大小,擴(kuò)容與壓測(cè)也是基于此。若有突發(fā)或者惡意攻擊訪問(wèn),都要將流量攔截在外。這部分功能不屬于業(yè)務(wù)側(cè),它是通用非業(yè)務(wù)的共性需求,所以我們將共性抽取為限流中間件。
方案設(shè)計(jì)
圖解:

Ratelimiter
- 谷歌Guava庫(kù)中的一個(gè)限流工具類,用于限制訪問(wèn)限制某一資源,令牌桶思想的一個(gè)實(shí)現(xiàn),可實(shí)現(xiàn)流量控制。
用到其兩個(gè)方法:
- create():創(chuàng)建一個(gè)RateLimiter實(shí)例,并設(shè)置該實(shí)例的限流速率(即允許每秒執(zhí)行的任務(wù)數(shù)),例如:
RateLimiter rateLimiter = RateLimiter.create(10);
上述代碼將創(chuàng)建一個(gè)每秒允許10個(gè)任務(wù)的RateLimiter實(shí)例。
tryAcquire():嘗試獲取一個(gè)許可,若獲取成功則返回true,否則返回false。 例如:
if (rateLimiter.tryAcquire()) {
// do something
}上述代碼將嘗試獲取一個(gè)許可,如果該實(shí)例當(dāng)前擁有足夠的許可,則執(zhí)行do something操作
使用自定義注解和AOP,攔截需要被限流保護(hù)的方法。
攔截后,用Ratelimiter做限流處理
- 使用自定義注解和AOP,攔截需要被限流保護(hù)的方法。
- 攔截后,用Ratelimiter做限流處理
代碼示例
1.自定義注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoRateLimiter {
// 每秒訪問(wèn)次數(shù)限制
public double rate() default 0d;
// 超頻后的觸發(fā)熔斷默認(rèn)響應(yīng)結(jié)果
public String returnJson() default "";
}2.常量類
public class Constant {
/**
* 全局Hashmap,用于存放【方法】對(duì)應(yīng)的限流類
* key: 類名 + 方法名
* value: 對(duì)應(yīng)限流類
*/
public static Map<String, RateLimiter> stringRateLimiterMap = Collections.synchronizedMap(new HashMap<String, RateLimiter>());
}3.切面邏輯處理
PS:切面中的實(shí)現(xiàn)細(xì)節(jié)/注解的使用已經(jīng)在統(tǒng)一白名單中間件 文章中詳細(xì)梳理過(guò)了,需要請(qǐng)移步
@Component
@Aspect
public class RateLimiterHandle {
// 切點(diǎn)
@Pointcut("@annotation(com.example.ratelimiteer.annotation.DoRateLimiter)")
public void aopPointcut() {
}
/**
* @param jp 切點(diǎn)
* @param doRateLimiter 注解實(shí)例
* @return 方法響應(yīng)結(jié)果
* @throws Throwable
*/
@Around(value = "aopPointcut()&& @annotation(doRateLimiter)")
public Object handle(ProceedingJoinPoint jp, DoRateLimiter doRateLimiter) throws Throwable {
// 判斷是否開啟限流
if (doRateLimiter.rate() == 0d) {
return jp.proceed();
}
// 構(gòu)建限流哈希Map的Key: 類名 + "." + "方法名"
String className = jp.getTarget().getClass().getName();
MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = jp.getTarget().getClass().getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
String methodName = method.getName();
String key = className + "." + methodName;
// 第一次走到該接口
if (!Constant.stringRateLimiterMap.containsKey(key)) {
Map<String, RateLimiter> rateLimiterMap = Constant.stringRateLimiterMap;
rateLimiterMap.put(key, RateLimiter.create(doRateLimiter.rate()));
}
// 判斷是否限流
RateLimiter rateLimiter = Constant.stringRateLimiterMap.get(key);
if (rateLimiter.tryAcquire()) {
// 未超過(guò)訪問(wèn)限制,則繼續(xù)執(zhí)行對(duì)應(yīng)業(yè)務(wù)邏輯
return jp.proceed();
} else {
// 超過(guò)訪問(wèn)限制,則返回默認(rèn)熔斷響應(yīng)結(jié)果(即注解中的returnJson)
return JSON.parseObject(doRateLimiter.returnJson());
}
}
}測(cè)試
controller如下,僅允許一個(gè)并發(fā)

壓測(cè)兩個(gè)并發(fā)

result:嘗試拿一個(gè)許可(RateLimiter.tryAcquire())未拿到(即超頻),返回注解中的returnJson()


到此這篇關(guān)于SpringBoot中間件封裝限流器的文章就介紹到這了,更多相關(guān)SpringBoot限流器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java讀寫鎖ReadWriteLock原理與應(yīng)用場(chǎng)景詳解
這篇文章主要介紹了Java讀寫鎖ReadWriteLock原理與應(yīng)用場(chǎng)景詳解,讀寫狀態(tài)的設(shè)計(jì),寫鎖的獲取與釋放,鎖降級(jí)需要的朋友可以參考下2023-02-02
http調(diào)用controller方法時(shí)openfeign執(zhí)行流程
這篇文章主要為大家介紹了http調(diào)用controller方法時(shí)openfeign執(zhí)行流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Java判斷對(duì)象是否為空(包括null ,"")的方法
這篇文章主要介紹了Java判斷對(duì)象是否為空(包括null ,"")的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
Java實(shí)現(xiàn)學(xué)生管理系統(tǒng)詳解流程
這篇文章主要為大家詳細(xì)介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
springboot加載一個(gè)properties文件轉(zhuǎn)換為map方式
這篇文章主要介紹了springboot加載一個(gè)properties文件轉(zhuǎn)換為map方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
mybatis-plus實(shí)現(xiàn)邏輯刪除的示例代碼
本文主要介紹了mybatis-plus實(shí)現(xiàn)邏輯刪除的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法
Redisson是一個(gè)用于Java的Redis客戶端,它提供了在分布式環(huán)境下操作Redis數(shù)據(jù)庫(kù)的簡(jiǎn)單、高效的方式,本文主要介紹了SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03

