Resilience4J Ratelimiter不会限制被注解的方法的访问。

huangapple go评论124阅读模式
英文:

Resilience4J Ratelimiter does not limit access of annotated method

问题

我正在使用resilience4j-spring-boot2库(io.github.resilience4j:resilience4j-spring-boot2,版本1.5.0)和Spring Boot版本2.3.1.RELEASE
我创建了一个RateLimiter,它应该只允许一个线程每30秒执行某个方法。但是,似乎没有边界(除了Tomcat服务器中运行的配置线程数)来调用该方法的线程。

我实现了一个简单的服务,它等待一定的时间,然后返回"Hello!":

@RequestMapping("/")
@RestController
public class ResilientService {

    @RateLimiter(name = "hello-rl")
    @GetMapping("/hello/{timeout}")
    public String hello(@PathVariable int timeout) throws InterruptedException {
        Thread.sleep(timeout);
        return "Hello!";
    }

}

并配置了RateLimiter如下:

resilience4j.ratelimiter:
  instances:
    hello-rl:
      limitForPeriod: 1  # 在一个限制刷新周期内可用的权限数
      limitRefreshPeriod: 30s  # 限制刷新的周期。在每个周期后,速率限制器将其权限计数设置回limitForPeriod值
      timeoutDuration: 30s  # 线程等待权限的默认等待时间

然后我使用了八个线程调用了该服务:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Slf4j
public class CircuitBreakerTest {

    @Autowired
    TestRestTemplate testRestTemplate;

    @LocalServerPort
    private int port;

    @Value("${server.address}")
    private String serverAddress;

    @Test
    public void t() throws InterruptedException {
        final ExecutorService executorService = Executors.newFixedThreadPool(8);

        for (int i : IntStream.rangeClosed(1, 8).toArray()) {
            executorService.execute(() -> log.error(callService()));
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);
    }

    private String callService() {
        return testRestTemplate.getForObject(
                "http://" + serverAddress + ":" + port + "/hello/5000",
                String.class
        );
    }
}

输出结果如下:

2020-08-07 10:41:10,095 ERROR [pool-1-thread-7] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-1] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-8] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-2] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-5] CircuitBreakerTest: Hello!
2020-08-07 10:41:15,104 ERROR [pool-1-thread-3] CircuitBreakerTest: Hello!
2020-08-07 10:41:15,121 ERROR [pool-1-thread-4] CircuitBreakerTest: Hello!
2020-08-07 10:41:15,121 ERROR [pool-1-thread-6] CircuitBreakerTest: Hello!

只有五个线程似乎被允许同时执行该方法,但我不知道为什么。Tomcat服务器正在运行超过8个线程。我犯了错误吗?还是我误解了RateLimiter应该如何工作?

英文:

I am using the resilience4j-spring-boot2 library (io.github.resilience4j:resilience4j-spring-boot2, version 1.5.0) and Spring Boot version 2.3.1.RELEASE.
I created a RateLimiter which should only let one thread execute a certain method every 30 seconds. However, there does not seem to be a boundary (except for configurated number of threads running in the Tomcat server) for threads to call the method.

I implemented a simple Service which waits for a given amount of time and then returns "Hello!":

@RequestMapping("/")
@RestController
public class ResilientService {

    @RateLimiter(name = "hello-rl")
    @GetMapping("/hello/{timeout}")
    public String hello(@PathVariable int timeout) throws InterruptedException {
        Thread.sleep(timeout);
        return "Hello!";
    }

}

And configured the Ratelimiter as follows

resilience4j.ratelimiter:
  instances:
    hello-rl:
      limitForPeriod: 1  # The number of permissions available during one limit refresh period
      limitRefreshPeriod: 30s  # The period of a limit refresh. After each period the rate limiter sets its permissions count back to the limitForPeriod value
      timeoutDuration: 30s  # The default wait time a thread waits for a permission

And I called the service with eight threads:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Slf4j
public class CircuitBreakerTest {

    @Autowired
    TestRestTemplate testRestTemplate;

    @LocalServerPort
    private int port;

    @Value("${server.address}")
    private String serverAddress;

    @Test
    public void t() throws InterruptedException {
        final ExecutorService executorService = Executors.newFixedThreadPool(8);

        for (int i : IntStream.rangeClosed(1, 8).toArray()) {
            executorService.execute(() -> log.error(callService()));
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);
    }

    private String callService() {
        return testRestTemplate.getForObject(
                "http://" + serverAddress + ":" + port + "/hello/5000",
                String.class
        );
    }

The output is as follows:

2020-08-07 10:41:10,095 ERROR [pool-1-thread-7] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-1] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-8] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-2] CircuitBreakerTest: Hello!
2020-08-07 10:41:10,095 ERROR [pool-1-thread-5] CircuitBreakerTest: Hello!
2020-08-07 10:41:15,104 ERROR [pool-1-thread-3] CircuitBreakerTest: Hello!
2020-08-07 10:41:15,121 ERROR [pool-1-thread-4] CircuitBreakerTest: Hello!
2020-08-07 10:41:15,121 ERROR [pool-1-thread-6] CircuitBreakerTest: Hello!

Only five threads seem to be allowed to execute the method at one time, but I don't know why. The Tomcat server is running with >8 threads.
Did I make a mistake? Or am I misunderstanding how the RateLimiter is supposed to work?

答案1

得分: 4

resilience4j-spring-boot2 内部使用了切面,所以需要将 spring-boot-starter-aop 依赖添加到项目中,以使注解起作用:

<dependency>
    <!-- 用于限流 -->
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>${resilience4j.version}</version>
</dependency>
<dependency>
    <!-- Resilience4j 所需 -->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

官方文档 在“Setup”部分中所述... 完全错过了那个部分。

英文:

resilience4j-spring-boot2 uses aspects internally, so it is needed to add the spring-boot-starter-aop dependency to the project for the annotations to work:

        &lt;dependency&gt;
            &lt;!-- for rate limiting --&gt;
            &lt;groupId&gt;io.github.resilience4j&lt;/groupId&gt;
            &lt;artifactId&gt;resilience4j-spring-boot2&lt;/artifactId&gt;
            &lt;version&gt;${resilience4j.version}&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;!-- needed for Resilience4j --&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-aop&lt;/artifactId&gt;
        &lt;/dependency&gt;

As stated in the official documentation right at the beginning ("Setup")... totally missed that.

huangapple
  • 本文由 发表于 2020年8月7日 16:52:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/63298452.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定