** ❃博主首页 :**
「码到三十五」
,同名公众号 :「码到三十五」,wx号 : 「liwu0213」
☠博主专栏 :
<源码解读>
<面试攻关>
♝博主的话 :搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基
调用速率限制是 Web API 中的常见要求,旨在防止滥用并确保公平使用资源。借助Spring Boot 中的 AOP,我们可以通过拦截方法调用并限制在特定时间范围内允许的请求数量来实现速率限制。
为了在 Spring Boot 中使用 AOP 实现速率限制:
- 定义自定义注释来标记应该限速的方法。
- 创建一个方面类,拦截用自定义注释注释的方法调用。
- 使用速率限制器组件来跟踪和执行速率限制。
- 处理速率限制超出的情况,如通过抛出自定义异常。
Spring Boot API 中的速率限制
可以使用各种技术在 Spring Boot API 中实现速率限制。一种常见的方法是使用 Spring AOP来拦截传入的请求并实施速率限制。
步骤 1 - 定义速率限制配置
创建一个配置类,在其中定义速率限制参数,例如允许的请求数和时间段。
@ConfigurationpublicclassRateLimitConfig{@Value("${rate.limit.requests}")privateint requests;@Value("${rate.limit.seconds}")privateint seconds;// Getters and setters}
第 2 步 - 创建速率限制方面
使用 Spring AOP 实现一个方面来拦截方法调用并强制执行速率限制。
@Aspect@ComponentpublicclassRateLimitAspect{@AutowiredprivateRateLimitConfig rateLimitConfig;@AutowiredprivateRateLimiter rateLimiter;@Around("@annotation(RateLimited)")publicObjectenforceRateLimit(ProceedingJoinPoint joinPoint)throwsThrowable{String key =getKey(joinPoint);if(!rateLimiter.tryAcquire(key, rateLimitConfig.getRequests(), rateLimitConfig.getSeconds())){thrownewRateLimitExceededException("Rate limit exceeded");}return joinPoint.proceed();}privateStringgetKey(ProceedingJoinPoint joinPoint){//为正在调用的方法生成唯一密钥//方法签名、用户ID、IP地址等。}}
步骤 3 — 定义 RateLimited 注释
创建自定义注释来标记应受速率限制的方法。
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceRateLimited{}
步骤 4 - 实施速率限制器
创建速率限制器组件,使用令牌桶算法或任何其他合适的算法来管理速率限制。
@ComponentpublicclassRateLimiter{privatefinalMap<String,RateLimitedSemaphore> semaphores =newConcurrentHashMap<>();publicbooleantryAcquire(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 =newRateLimitedSemaphore(requests);
newSemaphore.setLastAcquireTime(currentTime);// Set last acquire timereturn newSemaphore;});// 校验 semaphore boolean acquired = semaphore.tryAcquire();if(acquired){
semaphore.setLastAcquireTime(currentTime);// 更新}return acquired;}privatevoidcleanupExpiredEntries(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();}}}privateclassRateLimitedSemaphoreextendsSemaphore{privatevolatilelong lastAcquireTime;publicRateLimitedSemaphore(intpermits){super(permits);}publiclonggetLastAcquireTime(){return lastAcquireTime;}publicvoidsetLastAcquireTime(long lastAcquireTime){this.lastAcquireTime = lastAcquireTime;}}}
步骤 5 - 注释控制器方法
用注解来注释应该进行速率限制的控制器方法 @RateLimited。
@RestControllerpublicclassMyController{@RateLimited@GetMapping("/api/resource")publicResponseEntity<String>getResource(){// Implementation}}
步骤 6 - 配置速率限制属性
application.properties在您的 或 中配置速率限制属性 application.yml。
rate.limit.requests=10
rate.limit.seconds=60
要按 IP 地址限制请求,可以从传入请求中提取 IP 地址并将其用作速率限制的密钥:
privateStringgetKey(HttpServletRequest request){String ipAddress = request.getRemoteAddr();return ipAddress;//用ID做key}
还需要修改enforceRateLimit 中的方法 RateLimitAspect 以将对象传递 HttpServletRequest 给 getKey 方法:
@Around("@annotation(RateLimited)")publicObjectenforceRateLimit(ProceedingJoinPoint joinPoint)throwsThrowable{ServletRequestAttributes requestAttributes =(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();String key =getKey(request);if(!rateLimiter.tryAcquire(key, rateLimitConfig.getRequests(), rateLimitConfig.getSeconds())){thrownewRateLimitExceededException("Rate limit exceeded");}return joinPoint.proceed();}
关注公众号[码到三十五]获取更多技术干货 !
版权归原作者 码到三十五 所有, 如有侵权,请联系我们删除。