24 Mart 2023 Cuma

Bucket4J Kullanımı

Giriş
Şu satırı dahil ederiz
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.ConsumptionProbe;
import io.github.bucket4j.Refill;
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>com.bucket4j</groupId>
  <artifactId>bucket4j-core</artifactId>
  <version>8.1.1</version>
</dependency>
Bucket Arayüzü
Açıklaması şöyle
Bucket is the Interface that defines the behavior of a rate-limiter — based on the Token Bucket algorithm. 
A bucket is created using a builder pattern.

Bucket bucket = Bucket.builder()
  .addLimit(...)
  .build();

To add a Limit to the bucket, we define a Bandwidth denoted by the following terms.

Capacity specifies how many tokens your bucket has.

Refill specifies how fast tokens can be refilled after being consumed from a bucket.

If we choose the interval refill, the bucket will wait until the whole period is elapsed before regenerating the whole token amount.

// generates 10 tokens each minute
Refill.intervally(10, Duration.ofMinutes(1));
Örnek
Elimizde şöyle bir kod olsun
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {

  @Autowired
  RateLimitInterceptor rateLimitInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(rateLimitInterceptor);
  }
}
Şöyle yaparız
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
  private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
  private long capacity = 10;
  private long tokens = 10;

  Bucket defaultBucket = Bucket.builder()
      .addLimit(Bandwidth.classic(capacity, 
                                  Refill.intervally(tokens, Duration.ofMinutes(1))))
      .build();

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler) throws Exception {
    String clientIP = getClientIP(request);
    logger.info(clientIP);

    Bucket bucketByClientIP;
    if (buckets.containsKey(clientIP)) {
      bucketByClientIP = buckets.get(clientIP);
    } else {
      bucketByClientIP = this.defaultBucket;
      buckets.put(clientIP, bucketByClientIP);
    }
    ConsumptionProbe probe = bucketByClientIP
      .tryConsumeAndReturnRemaining(1);
    if (probe.isConsumed()) {
      response.addHeader("X-Rate-Limit-Remaining",
          Long.toString(probe.getRemainingTokens()));
      return true;
    }
    response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); // 429
    response.addHeader("X-Rate-Limit-Retry-After-Milliseconds",
        Long.toString(TimeUnit.NANOSECONDS.toMillis(probe.getNanosToWaitForRefill())));

    return false;
  }
}
Açıklaması şöyle
The magic of this library is in the isConsumed() method. After asking the bucket to consume a token from the basket, we test whether the token was consumed. If true, the limit was not exceeded, and the API allows the client to consume the endpoint. Otherwise, the limit was exceeded, and we rejected the request, returning an HTTP error code of 429 to the client.
IP bulma kodu şöyledir
private String getClientIP(HttpServletRequest request) {
    String ip = request.getHeader("X-FORWARDED-FOR");

    if (ip == null || ip.isEmpty()) {
      ip = request.getRemoteAddr();
    }
    return ip;
  }


Hiç yorum yok:

Yorum Gönder