SpringBoot中基于AOP和Semaphore實(shí)現(xiàn)API限流
為了在 Spring Boot 中使用 AOP 實(shí)現(xiàn)速率限制:
- 定義自定義注釋來標(biāo)記應(yīng)該限速的方法。
- 創(chuàng)建一個(gè)方面類,攔截用自定義注釋注釋的方法調(diào)用。
- 使用速率限制器組件來跟蹤和執(zhí)行速率限制。
- 處理速率限制超出的情況,如通過拋出自定義異常。
Spring Boot API 中的速率限制
可以使用各種技術(shù)在 Spring Boot API 中實(shí)現(xiàn)速率限制。一種常見的方法是使用 Spring AOP來攔截傳入的請(qǐng)求并實(shí)施速率限制。
步驟 1 - 定義速率限制配置
創(chuàng)建一個(gè)配置類,在其中定義速率限制參數(shù),例如允許的請(qǐ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 實(shí)現(xiàn)一個(gè)方面來攔截方法調(diào)用并強(qiáng)制執(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)建自定義注釋來標(biāo)記應(yīng)受速率限制的方法。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimited { }
步驟 4 - 實(shí)施速率限制器
創(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(); // 計(jì)算時(shí)間窗口 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; }); // 校驗(yàn) 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)該進(jìn)行速率限制的控制器方法 @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 地址限制請(qǐng)求,可以從傳入請(qǐng)求中提取 IP 地址并將其用作速率限制的密鑰:
private String getKey(HttpServletRequest request) { String ipAddress = request.getRemoteAddr(); return ipAddress; //用ID做key }
還需要修改enforceRateLimit 中的方法 RateLimitAspect 以將對(duì)象傳遞 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實(shí)現(xiàn)API限流的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot實(shí)現(xiàn)API限流的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JPA如何設(shè)置表名和實(shí)體名,表字段與實(shí)體字段的對(duì)應(yīng)
這篇文章主要介紹了JPA如何設(shè)置表名和實(shí)體名,表字段與實(shí)體字段的對(duì)應(yīng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11MyBatis使用注解開發(fā)和無主配置文件開發(fā)的情況
這篇文章主要介紹了MyBatis使用注解開發(fā)和無主配置文件開發(fā)的情況,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03Java基礎(chǔ)之三大控制流程結(jié)構(gòu)
這篇文章主要介紹了Java基礎(chǔ)之三大控制流程結(jié)構(gòu),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04關(guān)于Jackson的JSON工具類封裝 JsonUtils用法
這篇文章主要介紹了關(guān)于Jackson的JSON工具類封裝 JsonUtils用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09Spring實(shí)戰(zhàn)之ServletContextResource訪問資源文件示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之ServletContextResource訪問資源文件,結(jié)合實(shí)例形式分析了spring使用ServletContextResource讀取與遍歷資源文件相關(guān)操作技巧,需要的朋友可以參考下2019-12-12