ホームページ  >  記事  >  Java  >  SpringBoot は RateLimiter をどのように使用して AOP を流れる電流を制限しますか?

SpringBoot は RateLimiter をどのように使用して AOP を流れる電流を制限しますか?

PHPz
PHPz転載
2023-05-10 23:46:201665ブラウズ

RateLimiter を使用して AOP を通過する電流を制限する

1. 依存関係の導入

<!-- guava 限流 -->
<dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>25.1-jre</version>
</dependency>

2. カスタム アノテーション

@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
public  @interface ServiceLimit { 
     String description()  default "";
}

3. AOP 実装クラス

@Component
@Scope
@Aspect
public class LimitAspect {
    每秒只发出5个令牌,此处是单进程服务的限流,内部采用令牌捅算法实现
    private static   RateLimiter rateLimiter = RateLimiter.create(5.0);
    
    //Service层切点  限流
    @Pointcut("@annotation(com.itstyle.seckill.common.aop.ServiceLimit)")  
    public void ServiceAspect() {
        
    }
    
    @Around("ServiceAspect()")
    public  Object around(ProceedingJoinPoint joinPoint) { 
        Boolean flag = rateLimiter.tryAcquire();
        Object obj = null;
        try {
            if(flag){
                obj = joinPoint.proceed();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } 
        return obj;
    } 
}

4.

@Override
@ServiceLimit
@Transactional
    public Result startSeckil(long seckillId,long userId) {
        //todo 操作
    }

SpringBoot の電流制限を使用する

電流制限の基本アルゴリズム

トークン バケットとリーキー バケット

  • リーキー バケット アルゴリズムの実装はキューに依存することが多く、リクエストが到着すると、キューがいっぱいでない場合は直接キューに入れられ、プロセッサがキューの先頭からリクエストを取り出します。固定周波数で処理します。リクエストの量が多い場合、キューがいっぱいになり、新しいリクエストは破棄されます。

  • トークンバケットアルゴリズムは、固定容量のトークンを保管するバケットであり、トークンは固定レートでバケットに追加されます。バケットに保存されるトークンの数には上限があり、超過すると破棄または拒否されます。トラフィックまたはネットワーク リクエストが到着すると、各リクエストはトークンを取得する必要があります。トークンを取得できる場合は、直接処理され、トークン バケットからトークンが削除されます。取得できない場合、リクエストはフロー制限され、直接破棄されるか、バッファ内で待機されます。

トークン バケットとリーキー バケットの比較

  • トークン バケットは、固定レートでバケットにトークンを追加します。リクエストが処理されるかどうかは、バケット内に十分なトークンがあるかどうかによって異なります。トークンの数がゼロに減少すると、新しいリクエストは拒否されます。リーキー バケットは一定の固定レートでリクエストを流出させ、受信リクエストのレートは任意です。受信リクエストの数が蓄積される リーキー バケットの容量に達すると、新しい受信リクエストは拒否されます;

  • トークン バケットは平均流入速度を制限し、突然のリクエストを許可します。サポート 一度に 3 トークンまたは 4 トークンを取得します。リーキー バケットは一定の流出レートを制限します。つまり、流出レートは固定された一定値です。たとえば、常に一定のレートで流出します。 1 ですが、一度は 1 で次回は 2 になることはできません。これにより、バースト流入率が平滑化されます。

  • トークン バケットは、ある程度のバーストを許容しますが、リーキーの主な目的は、バケットは流出速度を平滑化するためのものです;

Guava RateLimiter

1. 依存関係

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version>
    <optional>true</optional>
</dependency>

2. サンプルコード

@Slf4j
@Configuration
public class RequestInterceptor implements HandlerInterceptor {
    // 根据字符串分不同的令牌桶, 每天自动清理缓存
    private static LoadingCache<String, RateLimiter> cachesRateLimiter = CacheBuilder.newBuilder()
            .maximumSize(1000)  //设置缓存个数
            /**
             * expireAfterWrite是在指定项在一定时间内没有创建/覆盖时,会移除该key,下次取的时候从loading中取
             * expireAfterAccess是指定项在一定时间内没有读写,会移除该key,下次取的时候从loading中取
             * refreshAfterWrite是在指定时间内没有被创建/覆盖,则指定时间过后,再次访问时,会去刷新该缓存,在新值没有到来之前,始终返回旧值
             * 跟expire的区别是,指定时间过后,expire是remove该key,下次访问是同步去获取返回新值;
             * 而refresh则是指定时间后,不会remove该key,下次访问会触发刷新,新值没有回来时返回旧值
             */
            .expireAfterAccess(1, TimeUnit.HOURS)
            .build(new CacheLoader<String, RateLimiter>() {
                @Override
                public RateLimiter load(String key) throws Exception {
                    // 新的字符串初始化 (限流每秒2个令牌响应)
                    return RateLimiter.create(2);
                }
            });
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        log.info("request请求地址path[{}] uri[{}]", request.getServletPath(), request.getRequestURI());
        try {
            String str = "hello";
            // 令牌桶
            RateLimiter rateLimiter = cachesRateLimiter.get(str);
            if (!rateLimiter.tryAcquire()) {
                System.out.println("too many requests.");
                return false;
            }
        } catch (Exception e) {
            // 解决拦截器的异常,全局异常处理器捕获不到的问题
            request.setAttribute("exception", e);
            request.getRequestDispatcher("/error").forward(request, response);
        }
        return true;
    }
}

3. Test

@RestController
@RequestMapping(value = "user")
public class UserController {
    @GetMapping
    public Result test2(){
        System.out.println("1111");
        return new Result(true,200,"");
    }
}

http://localhost:8080/user/

結果クラスがない場合は、単に文字列を返すことができます

#4. テスト結果

SpringBoot は RateLimiter をどのように使用して AOP を流れる電流を制限しますか?

その他

作成# #RateLimiter には 2 つのファクトリーメソッドが用意されています。

1 つはスムーズなバースト電流制限です

RateLimiter r = RateLimiter.create(5); //项目启动,直接允许5个令牌

1 つはスムーズな予熱電流制限です

RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS); //项目启动后3秒后才会到达设置的2个令牌

欠点

RateLimiter は、クラスターが必要な場合、単一マシンの電流制限にのみ使用できます。電流制限を行うには、redis または Alibaba のオープンソース センチネル ミドルウェアを導入する必要があります。

rree

以上がSpringBoot は RateLimiter をどのように使用して AOP を流れる電流を制限しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。