When designing the system, we will have an estimated capacity of the system, which exceeds the TPS/QPS that the system can withstand for a long time. threshold, the system may be overwhelmed, eventually causing the entire service to become unavailable. In order to avoid this situation, we need to limit the flow of interface requests.
So, we can protect the system or avoid unnecessary waste of resources by limiting the rate of concurrent access requests or the number of requests within a time window. Once the rate limit is reached, service can be denied. Queue or wait.
The system has an interface for obtaining mobile phone SMS verification codes. Because it is an open interface, in order to prevent users from constantly sending Request to obtain the verification code to prevent malicious interface brushing, so the simplest counter method is used to limit the current, limiting each IP to only one request per minute, and then the time window limit for each other mobile phone number is through the business Make judgments logically. Generally, some interfaces have a relatively large number of visits and may overwhelm the system, so traffic restrictions need to be added! Such as: flash sale, etc...
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimiter { /** * 限流key */ String key() default Constants.RATE_LIMIT_KEY; /** * 限流时间,单位秒 */ int time() default 60; /** * 限流次数 */ int count() default 100; /** * 限流类型 */ LimitType limitType() default LimitType.DEFAULT; /** * 限流后返回的文字 */ String limitMsg() default "访问过于频繁,请稍候再试"; }
@Aspect @Component public class RateLimiterAspect { private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); @Autowired private RedisUtils redisUtils; @Before("@annotation(rateLimiter)") public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { int time = rateLimiter.time(); int count = rateLimiter.count(); long total = 1L; String combineKey = getCombineKey(rateLimiter, point); try { if(redisUtils.hasKey(combineKey)){ total = redisUtils.incr(combineKey,1); //请求进来,对应的key加1 if(total > count) throw new ServiceRuntimeException(rateLimiter.limitMsg()); }else{ redisUtils.set(combineKey,1,time); //初始化key } } catch (ServiceRuntimeException e) { throw e; } catch (Exception e) { throw new ServiceRuntimeException("网络繁忙,请稍候再试"); } } /** * 获取限流key * @param rateLimiter * @param point * @return */ public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); if (rateLimiter.limitType() == LimitType.IP) { stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-"); } MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); Class<?> targetClass = method.getDeclaringClass(); stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); return stringBuffer.toString(); } }
@RestController public class TestController { @RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分钟内只能请求一次,请稍后重试") @GetMapping("/hello") public ResultMsg hello() { return ResultMsg.success("Hello World!"); } }
1) The first time sent, the result is returned normally
2) The second time sent within one minute, an error is returned, and the current limit prompt
The above is the AOP Redis
solution to implement interface current limiting. Have you given up on it?
There are other current limiting methods, such as sliding window current limiting method (more rigorous than counter), token bucket, etc... Interested friends can learn about it.
The above is the detailed content of Interface current limiting, all cool operations are here!. For more information, please follow other related articles on the PHP Chinese website!