Use Guava in spring boot to implement current limiting based on token bucket (simple and easy to use)

The current limit is detailed, and there are many famous halls. This kind of algorithm, that kind of algorithm, this kind of strategy, that kind of strategy. There is no absolute silver bullet. It must be realized in combination with actual scenarios. The simplest, use Google's Guava, a few lines of code. You can gracefully limit the current to an interface.

Token bucket algorithm

Use Guava in springboot to implement current limit based on token bucket

The popular understanding is that there is a fixed-size bucket, and the faucet keeps dripping water in it at a certain frequency. When the water is full, it won't drip. Each time the client makes a request, it must first try to take out at least "a drop of water" from the bucket in order to process the business. Because the size of the bucket is fixed, the faucet drip frequency is fixed. Thus, the access flow of the data interface is guaranteed.

Guava

A tool library of Google contains a large number of Java tool classes, such as hash algorithms, string processing, collections, and so on. . .

https://github.com/google/guava

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
 <groupId>com.google.guava</groupId>
 <artifactId>guava</artifactId>
 <version>29.0-jre</version>
</dependency>

Rate limiter RateLimiter

/**
 * 创建一个限速器,每1秒,产生2.5个令牌 
 */
RateLimiter rateLimiter = RateLimiter.create(2.5, 1, TimeUnit.SECONDS);

/**
 * 尝试获取1个令牌,如果没有,会阻塞当前线程。直到获取成功返回。
 * 返回值是,阻塞的秒数
 */
double waitSeconds = rateLimiter.acquire();

/**
 * 尝试获取1个令牌,不会阻塞当前线程。
 * 立即返回是否获取成功。
 */
boolean success = rateLimiter.tryAcquire();

Well, this is the core code. Just 3 lines. First create a speed limiter and specify the frequency of token production. There are two core methods, blocking to obtain tokens and non-blocking to obtain tokens. The code is also easy to understand.

Overloaded method

Regardless of blocking or non-blocking acquisition of tokens, they have several overloaded methods. At first glance, it is clear that you can set the number of tokens obtained and the blocking time.

public double acquire(int permits)

public boolean tryAcquire(Duration timeout)
public boolean tryAcquire(int permits)
public boolean tryAcquire(long timeout, TimeUnit unit)
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) 
public boolean tryAcquire(int permits, Duration timeout)

Controller, which is the rate-limited interface

@RestController
@RequestMapping("/test")
public class TestController {
 
 @GetMapping
 public Object test () {
  return Collections.singletonMap("success", "true");
 }
}

RateLimiterInterceptor, responsible for implementing rate limiting logic

import java.nio.charset.StandardCharsets;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.MediaType;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.google.common.util.concurrent.RateLimiter;

public class RateLimiterInterceptor extends HandlerInterceptorAdapter {

 private final RateLimiter rateLimiter;

 /**
  * 通过构造函数初始化限速器
  */
 public RateLimiterInterceptor(RateLimiter rateLimiter) {
  super();
  this.rateLimiter = rateLimiter;
 }

 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  
  if(this.rateLimiter.tryAcquire()) {
   /**
    * 成功获取到令牌
    */
   return true;
  }

  /**
   * 获取失败,直接响应“错误信息”
   * 也可以通过抛出异常,通过全全局异常处理器响应客户端
   */
  response.setCharacterEncoding(StandardCharsets.UTF_8.name());
  response.setContentType(MediaType.TEXT_PLAIN_VALUE);
  response.getWriter().write("服务器繁忙");
  return false;
 }
}

Interceptor configuration

import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.google.common.util.concurrent.RateLimiter;

import io.springboot.jwt.web.interceptor.RateLimiterInterceptor;


@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
 
 @Override
 public void addInterceptors(InterceptorRegistry registry) {
  /**
   * test接口,1秒钟生成1个令牌,也就是1秒中允许一个人访问
   */
  registry.addInterceptor(new RateLimiterInterceptor(RateLimiter.create(1, 1, TimeUnit.SECONDS)))
   .addPathPatterns("/test");
 }
 
}

The client demonstrates the current limiting effect

Guess you like

Origin blog.csdn.net/qq_31905135/article/details/109264487
Recommended