SpringBoot中基于AOP和Semaphore實現(xiàn)API限流
為了在 Spring Boot 中使用 AOP 實現(xiàn)速率限制:
- 定義自定義注釋來標記應(yīng)該限速的方法。
- 創(chuàng)建一個方面類,攔截用自定義注釋注釋的方法調(diào)用。
- 使用速率限制器組件來跟蹤和執(zhí)行速率限制。
- 處理速率限制超出的情況,如通過拋出自定義異常。
Spring Boot API 中的速率限制
可以使用各種技術(shù)在 Spring Boot API 中實現(xiàn)速率限制。一種常見的方法是使用 Spring AOP來攔截傳入的請求并實施速率限制。
步驟 1 - 定義速率限制配置
創(chuàng)建一個配置類,在其中定義速率限制參數(shù),例如允許的請求數(shù)和時間段。
@Configuration
public class RateLimitConfig {
@Value("${rate.limit.requests}")
private int requests;
@Value("${rate.limit.seconds}")
private int seconds;
// Getters and setters
}
步驟 2 - 創(chuàng)建速率限制方面
使用 Spring AOP 實現(xiàn)一個方面來攔截方法調(diào)用并強制執(zhí)行速率限制。
@Aspect
@Component
public class RateLimitAspect {
@Autowired
private RateLimitConfig rateLimitConfig;
@Autowired
private RateLimiter rateLimiter;
@Around("@annotation(RateLimited)")
public Object enforceRateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
String key = getKey(joinPoint);
if (!rateLimiter.tryAcquire(key, rateLimitConfig.getRequests(), rateLimitConfig.getSeconds())) {
throw new RateLimitExceededException("Rate limit exceeded");
}
return joinPoint.proceed();
}
private String getKey(ProceedingJoinPoint joinPoint) {
//為正在調(diào)用的方法生成唯一密鑰
//方法簽名、用戶ID、IP地址等。
}
}
步驟 3 — 定義 RateLimited 注釋
創(chuàng)建自定義注釋來標記應(yīng)受速率限制的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimited {
}
步驟 4 - 實施速率限制器
創(chuàng)建速率限制器組件,使用令牌桶算法或任何其他合適的算法來管理速率限制。
@Component
public class RateLimiter {
private final Map<String,RateLimitedSemaphore> semaphores = new ConcurrentHashMap<>();
public boolean tryAcquire(String key, int requests, int seconds) {
long currentTime = System.currentTimeMillis();
// 計算時間窗口
long startTime = currentTime - seconds * 1000;
// 過期刪除
cleanupExpiredEntries(startTime);
// 獲取semaphore
RateLimitedSemaphore semaphore = semaphores.computeIfAbsent(key, k -> {
RateLimitedSemaphore newSemaphore = new RateLimitedSemaphore(requests);
newSemaphore.setLastAcquireTime(currentTime); // Set last acquire time
return newSemaphore;
});
// 校驗 semaphore
boolean acquired = semaphore.tryAcquire();
if (acquired) {
semaphore.setLastAcquireTime(currentTime);
// 更新
}
return acquired;
}
private void cleanupExpiredEntries(long startTime) {
Iterator<Map.Entry<String, RateLimitedSemaphore>> iterator = semaphores.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, RateLimitedSemaphore> entry = iterator.next();
String key = entry.getKey();
RateLimitedSemaphore semaphore = entry.getValue();
if (semaphore.getLastAcquireTime() < startTime) {
iterator.remove();
}
}
}
private class RateLimitedSemaphore extends Semaphore {
private volatile long lastAcquireTime;
public RateLimitedSemaphore(int permits) {
super(permits);
}
public long getLastAcquireTime() {
return lastAcquireTime;
}
public void setLastAcquireTime(long lastAcquireTime) {
this.lastAcquireTime = lastAcquireTime;
}
}
}
步驟 5 - 注釋控制器方法
用注解來注釋應(yīng)該進行速率限制的控制器方法 @RateLimited。
@RestController
public class MyController {
@RateLimited
@GetMapping("/api/resource")
public ResponseEntity<String> getResource() {
// Implementation
}
}
步驟 6 - 配置速率限制屬性
application.properties在您的 或 中配置速率限制屬性 application.yml。
rate.limit.requests=10 rate.limit.seconds=60
要按 IP 地址限制請求,可以從傳入請求中提取 IP 地址并將其用作速率限制的密鑰:
private String getKey(HttpServletRequest request) {
String ipAddress = request.getRemoteAddr();
return ipAddress; //用ID做key
}
還需要修改enforceRateLimit 中的方法 RateLimitAspect 以將對象傳遞 HttpServletRequest 給 getKey 方法:
@Around("@annotation(RateLimited)")
public Object enforceRateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String key = getKey(request);
if (!rateLimiter.tryAcquire(key, rateLimitConfig.getRequests(), rateLimitConfig.getSeconds())) {
throw new RateLimitExceededException("Rate limit exceeded");
}
return joinPoint.proceed();
}
以上就是SpringBoot中基于AOP和Semaphore實現(xiàn)API限流的詳細內(nèi)容,更多關(guān)于SpringBoot實現(xiàn)API限流的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JPA如何設(shè)置表名和實體名,表字段與實體字段的對應(yīng)
這篇文章主要介紹了JPA如何設(shè)置表名和實體名,表字段與實體字段的對應(yīng),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
MyBatis使用注解開發(fā)和無主配置文件開發(fā)的情況
這篇文章主要介紹了MyBatis使用注解開發(fā)和無主配置文件開發(fā)的情況,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
Java基礎(chǔ)之三大控制流程結(jié)構(gòu)
這篇文章主要介紹了Java基礎(chǔ)之三大控制流程結(jié)構(gòu),文中有非常詳細的代碼示例,對正在學習java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
關(guān)于Jackson的JSON工具類封裝 JsonUtils用法
這篇文章主要介紹了關(guān)于Jackson的JSON工具類封裝 JsonUtils用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Spring實戰(zhàn)之ServletContextResource訪問資源文件示例
這篇文章主要介紹了Spring實戰(zhàn)之ServletContextResource訪問資源文件,結(jié)合實例形式分析了spring使用ServletContextResource讀取與遍歷資源文件相關(guān)操作技巧,需要的朋友可以參考下2019-12-12

