インターフェイス電流制限、すべての優れた操作がここにあります。

リリース: 2023-07-26 14:22:33
転載
1136 人が閲覧しました

電流制限を行う理由

システムを設計する際には、システムが耐えられる TPS/QPS を超えるシステムの容量を見積もることになります。しきい値を長期間にわたって超えると、システムに負荷がかかり、最終的にはサービス全体が利用できなくなる可能性があります。この状況を回避するには、インターフェースリクエストのフローを制限する必要があります。

したがって、同時アクセス リクエストのレートまたは時間枠内のリクエストの数を制限することで、システムを保護したり、リソースの不要な浪費を回避したりできます。レート制限に達すると、サービスは拒否される可能性があります。キューまたは待ってください。

電流制限の背景

システムには、携帯電話の SMS 認証コードを取得するためのインターフェイスがあり、オープン インターフェイスであるため、悪意のあるインターフェイスのブラッシングを防ぐために、ユーザーが検証コードを取得するためのリクエストを継続的に送信できないようにするため、最も単純なカウンター方法を使用して電流を制限し、各 IP を 1 分あたり 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>
ログイン後にコピー

2. カスタム電流制限注釈
@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 "访问过于频繁,请稍候再试";
}
ログイン後にコピー

3. 電流制限の側面
@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();
    }

}
ログイン後にコピー

4. 簡単な記述テスト用インターフェイス##
@RestController
public class TestController {

    @RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分钟内只能请求一次,请稍后重试")
    @GetMapping("/hello")
    public ResultMsg hello() {
        return ResultMsg.success("Hello World!");
    }
}
ログイン後にコピー

##5. グローバル例外インターセプト
@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("系统异常");
    }

}
ログイン後にコピー

6. インターフェイス テスト

1) 初めて送信すると、正常に結果が返されます。

インターフェイス電流制限、すべての優れた操作がここにあります。

2) 1 分以内に 2 回目に送信すると、エラーが返され、電流制限プロンプト

インターフェイス電流制限、すべての優れた操作がここにあります。

上記は、インターフェイス電流制限を実装するための AOP Redis ソリューションです。もう諦めていませんか?

スライディング ウィンドウ電流制限方法 (カウンターよりも厳密)、トークン バケットなど、他の電流制限方法もあります。興味のある友人はそれについて学ぶことができます。


以上がインターフェイス電流制限、すべての優れた操作がここにあります。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:Java学习指南
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート