本文最后更新于:2025年6月23日 晚上
Guava实现接口限流
guava是google开发的工具包,包含很多工具。今天通过它的RateLimiter
+注解来实现接口限流(底层基于令牌桶算法)。
注意:这通常用来实现单体项目的接口限流,分布式应用中,有更高级的sentinel
可供选择,可根据需要使用。
Maven依赖
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
注解类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimit {
int count() default 1;
int time() default 1; }
|
也可加一个用户级别的限流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimitForUserAgent {
int count() default 10;
int time() default 1; }
|
代理切面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| import com.google.common.util.concurrent.RateLimiter; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap;
@Aspect @Component public class RateLimitAspect {
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
@Before(value = "@annotation(rateLimit)") public void checkRateLimit(JoinPoint joinPoint,RateLimit rateLimit) { String key = joinPoint.getSignature().toLongString(); RateLimiter rateLimiter = limiters.computeIfAbsent(key, k -> RateLimiter.create(rateLimit.count() / (double) rateLimit.time())); if (!rateLimiter.tryAcquire()) { throw new RuntimeException("请求过于频繁"); } }
@Before(value = "@annotation(rateLimit)") public void checkRateLimitForUserAgent(JoinPoint joinPoint,RateLimitForUserAgent rateLimit) { HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); String userAgent = request.getHeader("User-Agent"); System.out.println("userAgent:"+userAgent); String key = joinPoint.getSignature().toLongString() + ":" +userAgent; RateLimiter rateLimiter = limiters.computeIfAbsent(key, k -> RateLimiter.create(rateLimit.count() / (double) rateLimit.time())); if (!rateLimiter.tryAcquire()) { throw new RequestFrequenceException("请求过于频繁"); } } }
|
全局异常处理
自定义请求太频繁异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import lombok.Getter;
@Getter public class RequestFrequenceException extends RuntimeException{
private final int code = 400;
public RequestFrequenceException(String message) { super(message); }
public RequestFrequenceException(String message, Throwable cause) { super(message, cause); }
public RequestFrequenceException(Throwable cause) { super(cause); } }
|
全局异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap; import java.util.Map;
@Slf4j @RestControllerAdvice public class MyAdvice {
@ExceptionHandler(RequestFrequenceException.class) public Object handleException(RequestFrequenceException e) { log.error("请求过于频繁"); return processResponse(e); }
private ResponseEntity<Map<String, Object>> processResponse(RequestFrequenceException e){ Map<String, Object> map = new HashMap<>(); map.put("code", e.getCode()); map.put("message", e.getMessage()); map.put("data", null);
return ResponseEntity.status(e.getCode()).body(map); } }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import com.event.config.RateLimit; import com.event.config.RateLimitForUserAgent; import org.springframework.web.bind.annotation.*;
@RestController @RequestMapping("/orders") public class OrderController {
@RateLimitForUserAgent(count = 1, time = 1) @PostMapping public String createOrder() { Long orderId = System.currentTimeMillis(); return "Order created with ID: " + orderId; } }
|