システムを設計する際には、システムが耐えられる TPS/QPS を超えるシステムの容量を見積もることになります。しきい値を長期間にわたって超えると、システムに負荷がかかり、最終的にはサービス全体が利用できなくなる可能性があります。この状況を回避するには、インターフェースリクエストのフローを制限する必要があります。
したがって、同時アクセス リクエストのレートまたは時間枠内のリクエストの数を制限することで、システムを保護したり、リソースの不要な浪費を回避したりできます。レート制限に達すると、サービスは拒否される可能性があります。キューまたは待ってください。
システムには、携帯電話の SMS 認証コードを取得するためのインターフェイスがあり、オープン インターフェイスであるため、悪意のあるインターフェイスのブラッシングを防ぐために、ユーザーが検証コードを取得するためのリクエストを継続的に送信できないようにするため、最も単純なカウンター方法を使用して電流を制限し、各 IP を 1 分あたり 1 つのリクエストのみに制限し、その後、他の携帯電話ごとに時間枠を制限します。ビジネスを通じた数字を論理的に判断する。一般に、一部のインターフェイスはアクセス数が比較的多く、システムに負荷を与える可能性があるため、トラフィック制限を追加する必要があります。例: フラッシュ セールなど...
<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!"); } }
@RestControllerAdvice public class GlobalExceptionHandler { private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 业务异常 */ @ExceptionHandler(ServiceRuntimeException.class) public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request) { return ResultMsg.error(e.getMessage()); } /** * 系统异常 */ @ExceptionHandler(Exception.class) public ResultMsg handleException(Exception e, HttpServletRequest request) { return ResultMsg.error("系统异常"); } }
1) 初めて送信すると、正常に結果が返されます。
2) 1 分以内に 2 回目に送信すると、エラーが返され、電流制限プロンプト
上記は、インターフェイス電流制限を実装するための AOP Redis
ソリューションです。もう諦めていませんか?
スライディング ウィンドウ電流制限方法 (カウンターよりも厳密)、トークン バケットなど、他の電流制限方法もあります。興味のある友人はそれについて学ぶことができます。
以上がインターフェイス電流制限、すべての優れた操作がここにあります。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。