diff --git a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/filter/reactive/AbstractReactiveFilter.java b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/filter/reactive/AbstractReactiveFilter.java index 53684693..0bffe2ed 100644 --- a/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/filter/reactive/AbstractReactiveFilter.java +++ b/bucket4j-spring-boot-starter/src/main/java/com/giffing/bucket4j/spring/boot/starter/filter/reactive/AbstractReactiveFilter.java @@ -2,9 +2,12 @@ import static java.nio.charset.StandardCharsets.UTF_8; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import com.giffing.bucket4j.spring.boot.starter.context.ConsumptionProbeHolder; +import com.giffing.bucket4j.spring.boot.starter.context.RateLimitCheck; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -22,9 +25,9 @@ @Data @Slf4j public class AbstractReactiveFilter { - + private final FilterConfiguration filterConfig; - + public AbstractReactiveFilter(FilterConfiguration filterConfig) { this.filterConfig = filterConfig; } @@ -32,37 +35,28 @@ public AbstractReactiveFilter(FilterConfiguration filterConfi protected boolean urlMatches(ServerHttpRequest request) { return request.getURI().getPath().matches(filterConfig.getUrl()); } - + protected Mono chainWithRateLimitCheck(ServerWebExchange exchange, ReactiveFilterChain chain) { log.debug("reate-limit-check;method:{};uri:{}", exchange.getRequest().getMethod(), exchange.getRequest().getURI()); ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); - List> asyncConsumptionProbes = filterConfig.getRateLimitChecks() - .stream() - .map(rl -> rl.rateLimit(request)) - .filter(cph -> cph != null && cph.getConsumptionProbeCompletableFuture() != null) - .map(cph -> Mono.fromFuture(cph.getConsumptionProbeCompletableFuture())) - .toList(); + List> asyncConsumptionProbes = new ArrayList<>(); + for (RateLimitCheck rlc : filterConfig.getRateLimitChecks()) { + ConsumptionProbeHolder cph = rlc.rateLimit(request); + if(cph != null && cph.getConsumptionProbeCompletableFuture() != null){ + asyncConsumptionProbes.add(Mono.fromFuture(cph.getConsumptionProbeCompletableFuture())); + if(filterConfig.getStrategy() == RateLimitConditionMatchingStrategy.FIRST){ + break; + } + } + } if(asyncConsumptionProbes.isEmpty()) { return chain.apply(exchange); } - AtomicInteger consumptionProbeCounter = new AtomicInteger(0); return Flux - .concat(asyncConsumptionProbes) - //.takeWhile(Objects::nonNull) - .doOnNext(cp -> consumptionProbeCounter.incrementAndGet()) - .takeWhile(cp -> shouldTakeMoreConsumptionProbe(consumptionProbeCounter)) - .reduce(this::reduceConsumptionProbe) - .flatMap(consumptionProbe -> handleConsumptionProbe(exchange, chain, response, consumptionProbe)); - - } - - protected boolean shouldTakeMoreConsumptionProbe(AtomicInteger consumptionProbeCounter) { - boolean shouldTakeMore = filterConfig.getStrategy().equals(RateLimitConditionMatchingStrategy.ALL) - || - (filterConfig.getStrategy().equals(RateLimitConditionMatchingStrategy.FIRST) && consumptionProbeCounter.get() == 1); - log.debug("take-more-probes:{};probe-index:{};matching-strategy:{}", shouldTakeMore, consumptionProbeCounter.get(), filterConfig.getStrategy()); - return shouldTakeMore; + .concat(asyncConsumptionProbes) + .reduce(this::reduceConsumptionProbe) + .flatMap(consumptionProbe -> handleConsumptionProbe(exchange, chain, response, consumptionProbe)); } protected ConsumptionProbe reduceConsumptionProbe(ConsumptionProbe x, ConsumptionProbe y) { @@ -72,7 +66,7 @@ protected ConsumptionProbe reduceConsumptionProbe(ConsumptionProbe x, Consumptio } else if(!y.isConsumed()) { result = y; } else { - result = x.getRemainingTokens() < y.getRemainingTokens() ? x : y; + result = x.getRemainingTokens() < y.getRemainingTokens() ? x : y; } log.debug("reduce-probes;result-isConsumed:{};result-getremainingTokens:{};x-isConsumed:{};x-getremainingTokens{};y-isConsumed:{};y-getremainingTokens{}", result.isConsumed(), result.getRemainingTokens(), @@ -80,15 +74,15 @@ protected ConsumptionProbe reduceConsumptionProbe(ConsumptionProbe x, Consumptio y.isConsumed(), y.getRemainingTokens()); return result; } - + protected Mono handleConsumptionProbe(ServerWebExchange exchange, ReactiveFilterChain chain, ServerHttpResponse response, ConsumptionProbe cp) { - log.debug("probe-results;isConsumed:{};remainingTokens:{};nanosToWaitForRefill:{};nanosToWaitForReset:{}", - cp.isConsumed(), - cp.getRemainingTokens(), - cp.getNanosToWaitForRefill(), + log.debug("probe-results;isConsumed:{};remainingTokens:{};nanosToWaitForRefill:{};nanosToWaitForReset:{}", + cp.isConsumed(), + cp.getRemainingTokens(), + cp.getNanosToWaitForRefill(), cp.getNanosToWaitForReset()); - + if(!cp.isConsumed()) { if(Boolean.FALSE.equals(filterConfig.getHideHttpResponseHeaders())) { filterConfig.getHttpResponseHeaders().forEach(response.getHeaders()::addIfAbsent); @@ -97,7 +91,7 @@ protected Mono handleConsumptionProbe(ServerWebExchange exchange, Reactive response.setStatusCode(filterConfig.getHttpStatusCode()); response.getHeaders().set("Content-Type", filterConfig.getHttpContentType()); DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(filterConfig.getHttpResponseBody().getBytes(UTF_8)); - return response.writeWith(Flux.just(buffer)); + return response.writeWith(Flux.just(buffer)); } else { return Mono.error(new ReactiveRateLimitException("HTTP ResponseBody is null")); } @@ -108,6 +102,4 @@ protected Mono handleConsumptionProbe(ServerWebExchange exchange, Reactive } return chain.apply(exchange); } - - } diff --git a/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/gateway/SpringCloudGatewayRateLimitFilterTest.java b/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/gateway/SpringCloudGatewayRateLimitFilterTest.java index 8b0f31f7..2f9fbebb 100644 --- a/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/gateway/SpringCloudGatewayRateLimitFilterTest.java +++ b/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/gateway/SpringCloudGatewayRateLimitFilterTest.java @@ -39,52 +39,52 @@ class SpringCloudGatewayRateLimitFilterTest { private GlobalFilter filter; - private FilterConfiguration configuration; - private RateLimitCheck rateLimitCheck1; - private RateLimitCheck rateLimitCheck2; - private RateLimitCheck rateLimitCheck3; + private FilterConfiguration configuration; + private RateLimitCheck rateLimitCheck1; + private RateLimitCheck rateLimitCheck2; + private RateLimitCheck rateLimitCheck3; private ServerWebExchange exchange; private GatewayFilterChain chain; - - + + private ServerHttpResponse serverHttpResponse; - + @BeforeEach - public void setup() throws URISyntaxException { - rateLimitCheck1 = mock(RateLimitCheck.class); - rateLimitCheck2 = mock(RateLimitCheck.class); - rateLimitCheck3 = mock(RateLimitCheck.class); - - exchange = Mockito.mock(ServerWebExchange.class); - - ServerHttpRequest serverHttpRequest = Mockito.mock(ServerHttpRequest.class); - URI uri = new URI("url"); - when(serverHttpRequest.getURI()).thenReturn(uri); + public void setup() throws URISyntaxException { + rateLimitCheck1 = mock(RateLimitCheck.class); + rateLimitCheck2 = mock(RateLimitCheck.class); + rateLimitCheck3 = mock(RateLimitCheck.class); + + exchange = Mockito.mock(ServerWebExchange.class); + + ServerHttpRequest serverHttpRequest = Mockito.mock(ServerHttpRequest.class); + URI uri = new URI("url"); + when(serverHttpRequest.getURI()).thenReturn(uri); when(exchange.getRequest()).thenReturn(serverHttpRequest); - + serverHttpResponse = Mockito.mock(ServerHttpResponse.class); - when(exchange.getResponse()).thenReturn(serverHttpResponse); - + when(exchange.getResponse()).thenReturn(serverHttpResponse); + chain = Mockito.mock(GatewayFilterChain.class); when(chain.filter(exchange)).thenReturn(Mono.empty()); - - configuration = new FilterConfiguration(); - configuration.setRateLimitChecks(Arrays.asList(rateLimitCheck1, rateLimitCheck2, rateLimitCheck3)); - configuration.setUrl(".*"); - filter = new SpringCloudGatewayRateLimitFilter(configuration); - } - - @Test + + configuration = new FilterConfiguration<>(); + configuration.setRateLimitChecks(Arrays.asList(rateLimitCheck1, rateLimitCheck2, rateLimitCheck3)); + configuration.setUrl(".*"); + filter = new SpringCloudGatewayRateLimitFilter(configuration); + } + + @Test void should_throw_rate_limit_exception_with_no_remaining_tokens() { - + configuration.setStrategy(RateLimitConditionMatchingStrategy.FIRST); - rateLimitConfig(0L, rateLimitCheck1); - HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); - when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); - - AtomicBoolean hasRateLimitError = new AtomicBoolean(false); + rateLimitConfig(0L, rateLimitCheck1); + HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); + + AtomicBoolean hasRateLimitError = new AtomicBoolean(false); Mono result = filter.filter(exchange, chain) .onErrorResume(ReactiveRateLimitException.class, (e) -> { hasRateLimitError.set(true); @@ -93,63 +93,62 @@ void should_throw_rate_limit_exception_with_no_remaining_tokens() { result.subscribe(); Assertions.assertTrue(hasRateLimitError.get()); } - + @Test void should_execute_all_checks_when_using_RateLimitConditionMatchingStrategy_All() throws URISyntaxException { - - configuration.setStrategy(RateLimitConditionMatchingStrategy.ALL); - - rateLimitConfig(30L, rateLimitCheck1); - rateLimitConfig(0L, rateLimitCheck2); - rateLimitConfig(0L, rateLimitCheck3); - - HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); - when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); - final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - - Mono result = filter.filter(exchange, chain); - assertThrows(ReactiveRateLimitException.class, () -> { - result.block(); - }); - + + configuration.setStrategy(RateLimitConditionMatchingStrategy.ALL); + + rateLimitConfig(30L, rateLimitCheck1); + rateLimitConfig(0L, rateLimitCheck2); + rateLimitConfig(0L, rateLimitCheck3); + + HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + + Mono result = filter.filter(exchange, chain); + assertThrows(ReactiveRateLimitException.class, () -> { + result.block(); + }); + verify(rateLimitCheck1, times(1)).rateLimit(any()); - verify(rateLimitCheck2, times(1)).rateLimit(any()); - verify(rateLimitCheck3, times(1)).rateLimit(any()); + verify(rateLimitCheck2, times(1)).rateLimit(any()); + verify(rateLimitCheck3, times(1)).rateLimit(any()); } @Test void should_execute_only_one_check_when_using_RateLimitConditionMatchingStrategy_FIRST() { - configuration.setStrategy(RateLimitConditionMatchingStrategy.FIRST); - - rateLimitConfig(30L, rateLimitCheck1); - rateLimitConfig(0L, rateLimitCheck2); - rateLimitConfig(10L, rateLimitCheck3); - - HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); - when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); - final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - + configuration.setStrategy(RateLimitConditionMatchingStrategy.FIRST); + + rateLimitConfig(30L, rateLimitCheck1); + rateLimitConfig(0L, rateLimitCheck2); + rateLimitConfig(10L, rateLimitCheck3); + + HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + Mono result = filter.filter(exchange, chain); result.block(); - - verify(httpHeaders, times(1)).set(any(), captor.capture()); - - List values = captor.getAllValues(); - Assertions.assertEquals("30", values.stream().findFirst().get()); - - verify(rateLimitCheck1, times(1)).rateLimit(any()); - verify(rateLimitCheck2, times(1)).rateLimit(any()); - verify(rateLimitCheck3, times(1)).rateLimit(any()); + + verify(httpHeaders, times(1)).set(any(), captor.capture()); + + List values = captor.getAllValues(); + Assertions.assertEquals("30", values.stream().findFirst().get()); + + verify(rateLimitCheck1, times(1)).rateLimit(any()); + verify(rateLimitCheck2, times(0)).rateLimit(any()); + verify(rateLimitCheck3, times(0)).rateLimit(any()); } - private void rateLimitConfig(Long remainingTokens, RateLimitCheck rateLimitCheck) { + private void rateLimitConfig(Long remainingTokens, RateLimitCheck rateLimitCheck) { ConsumptionProbeHolder consumptionHolder = Mockito.mock(ConsumptionProbeHolder.class); - ConsumptionProbe probe = Mockito.mock(ConsumptionProbe.class); - when(probe.isConsumed()).thenReturn(remainingTokens > 0 ? true : false); + ConsumptionProbe probe = Mockito.mock(ConsumptionProbe.class); + when(probe.isConsumed()).thenReturn(remainingTokens > 0); when(probe.getRemainingTokens()).thenReturn(remainingTokens); when(consumptionHolder.getConsumptionProbeCompletableFuture()) - .thenReturn(CompletableFuture.completedFuture(probe)); - when(rateLimitCheck.rateLimit(any())).thenReturn(consumptionHolder); + .thenReturn(CompletableFuture.completedFuture(probe)); + when(rateLimitCheck.rateLimit(any())).thenReturn(consumptionHolder); } - } diff --git a/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/webflux/WebfluxRateLimitFilterTest.java b/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/webflux/WebfluxRateLimitFilterTest.java index 24213f5e..aa2b9350 100644 --- a/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/webflux/WebfluxRateLimitFilterTest.java +++ b/bucket4j-spring-boot-starter/src/test/java/com/giffing/bucket4j/spring/boot/starter/webflux/WebfluxRateLimitFilterTest.java @@ -38,50 +38,50 @@ class WebfluxRateLimitFilterTest { private WebfluxWebFilter filter; private FilterConfiguration configuration; - private RateLimitCheck rateLimitCheck1; - private RateLimitCheck rateLimitCheck2; - private RateLimitCheck rateLimitCheck3; + private RateLimitCheck rateLimitCheck1; + private RateLimitCheck rateLimitCheck2; + private RateLimitCheck rateLimitCheck3; private ServerWebExchange exchange; private WebFilterChain chain; - - + + private ServerHttpResponse serverHttpResponse; - + @BeforeEach - public void setup() throws URISyntaxException { - rateLimitCheck1 = mock(RateLimitCheck.class); - rateLimitCheck2 = mock(RateLimitCheck.class); - rateLimitCheck3 = mock(RateLimitCheck.class); - - exchange = Mockito.mock(ServerWebExchange.class); - - ServerHttpRequest serverHttpRequest = Mockito.mock(ServerHttpRequest.class); - URI uri = new URI("url"); - when(serverHttpRequest.getURI()).thenReturn(uri); + public void setup() throws URISyntaxException { + rateLimitCheck1 = mock(RateLimitCheck.class); + rateLimitCheck2 = mock(RateLimitCheck.class); + rateLimitCheck3 = mock(RateLimitCheck.class); + + exchange = Mockito.mock(ServerWebExchange.class); + + ServerHttpRequest serverHttpRequest = Mockito.mock(ServerHttpRequest.class); + URI uri = new URI("url"); + when(serverHttpRequest.getURI()).thenReturn(uri); when(exchange.getRequest()).thenReturn(serverHttpRequest); - + serverHttpResponse = Mockito.mock(ServerHttpResponse.class); - when(exchange.getResponse()).thenReturn(serverHttpResponse); - + when(exchange.getResponse()).thenReturn(serverHttpResponse); + chain = Mockito.mock(WebFilterChain.class); when(chain.filter(exchange)).thenReturn(Mono.empty()); - - configuration = new FilterConfiguration<>(); - configuration.setRateLimitChecks(Arrays.asList(rateLimitCheck1, rateLimitCheck2, rateLimitCheck3)); - configuration.setUrl(".*"); - filter = new WebfluxWebFilter(configuration); - } + + configuration = new FilterConfiguration<>(); + configuration.setRateLimitChecks(Arrays.asList(rateLimitCheck1, rateLimitCheck2, rateLimitCheck3)); + configuration.setUrl(".*"); + filter = new WebfluxWebFilter(configuration); + } @Test void should_throw_rate_limit_exception_with_no_remaining_tokens() { - + configuration.setStrategy(RateLimitConditionMatchingStrategy.FIRST); - rateLimitConfig(0L, rateLimitCheck1); - HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); - when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); - - AtomicBoolean hasRateLimitError = new AtomicBoolean(false); + rateLimitConfig(0L, rateLimitCheck1); + HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); + + AtomicBoolean hasRateLimitError = new AtomicBoolean(false); Mono result = filter.filter(exchange, chain) .onErrorResume(ReactiveRateLimitException.class, (e) -> { hasRateLimitError.set(true); @@ -90,63 +90,63 @@ public void setup() throws URISyntaxException { result.subscribe(); Assertions.assertTrue(hasRateLimitError.get()); } - + @Test void should_execute_all_checks_when_using_RateLimitConditionMatchingStrategy_All() throws URISyntaxException { - - configuration.setStrategy(RateLimitConditionMatchingStrategy.ALL); - - rateLimitConfig(30L, rateLimitCheck1); - rateLimitConfig(0L, rateLimitCheck2); - rateLimitConfig(0L, rateLimitCheck3); - - HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); - when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); - - Assertions.assertThrows(ReactiveRateLimitException.class, () -> { - Mono result = filter.filter(exchange, chain); - result.block(); - }); - + + configuration.setStrategy(RateLimitConditionMatchingStrategy.ALL); + + rateLimitConfig(30L, rateLimitCheck1); + rateLimitConfig(0L, rateLimitCheck2); + rateLimitConfig(0L, rateLimitCheck3); + + HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); + + Assertions.assertThrows(ReactiveRateLimitException.class, () -> { + Mono result = filter.filter(exchange, chain); + result.block(); + }); + verify(rateLimitCheck1).rateLimit(any()); - verify(rateLimitCheck2).rateLimit(any()); - verify(rateLimitCheck3).rateLimit(any()); + verify(rateLimitCheck2).rateLimit(any()); + verify(rateLimitCheck3).rateLimit(any()); } @Test void should_execute_only_one_check_when_using_RateLimitConditionMatchingStrategy_FIRST() { - - configuration.setStrategy(RateLimitConditionMatchingStrategy.FIRST); - - rateLimitConfig(30L, rateLimitCheck1); - rateLimitConfig(0L, rateLimitCheck2); - rateLimitConfig(10L, rateLimitCheck3); - - HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); - when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); - - Mono result = filter.filter(exchange, chain); + + configuration.setStrategy(RateLimitConditionMatchingStrategy.FIRST); + + rateLimitConfig(30L, rateLimitCheck1); + rateLimitConfig(0L, rateLimitCheck2); + rateLimitConfig(10L, rateLimitCheck3); + + HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + when(serverHttpResponse.getHeaders()).thenReturn(httpHeaders); + + Mono result = filter.filter(exchange, chain); result.block(); - + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(httpHeaders, times(1)).set(any(), captor.capture()); - - List values = captor.getAllValues(); - Assertions.assertEquals("30", values.stream().findFirst().get()); - - verify(rateLimitCheck1).rateLimit(any()); - verify(rateLimitCheck2).rateLimit(any()); - verify(rateLimitCheck3).rateLimit(any()); + verify(httpHeaders, times(1)).set(any(), captor.capture()); + + List values = captor.getAllValues(); + Assertions.assertEquals("30", values.stream().findFirst().get()); + + verify(rateLimitCheck1, times(1)).rateLimit(any()); + verify(rateLimitCheck2, times(0)).rateLimit(any()); + verify(rateLimitCheck3, times(0)).rateLimit(any()); } - private void rateLimitConfig(Long remainingTokens, RateLimitCheck rateLimitCheck) { + private void rateLimitConfig(Long remainingTokens, RateLimitCheck rateLimitCheck) { ConsumptionProbeHolder consumptionHolder = Mockito.mock(ConsumptionProbeHolder.class); - ConsumptionProbe probe = Mockito.mock(ConsumptionProbe.class); - when(probe.isConsumed()).thenReturn(remainingTokens > 0 ? true : false); + ConsumptionProbe probe = Mockito.mock(ConsumptionProbe.class); + when(probe.isConsumed()).thenReturn(remainingTokens > 0); when(probe.getRemainingTokens()).thenReturn(remainingTokens); when(consumptionHolder.getConsumptionProbeCompletableFuture()) - .thenReturn(CompletableFuture.completedFuture(probe)); - when(rateLimitCheck.rateLimit(any())).thenReturn(consumptionHolder); + .thenReturn(CompletableFuture.completedFuture(probe)); + when(rateLimitCheck.rateLimit(any())).thenReturn(consumptionHolder); } - + } diff --git a/examples/gateway/src/main/resources/application.yml b/examples/gateway/src/main/resources/application.yml index 8111cb7c..d90a6f77 100644 --- a/examples/gateway/src/main/resources/application.yml +++ b/examples/gateway/src/main/resources/application.yml @@ -1,21 +1,35 @@ -spring: - cache: - type: hazelcast -management: - endpoints: - web: - exposure: - include: "*" -bucket4j: default-tags: - expression: "1" key: xx filters: - - metrics: - types: - - consumed-counter - - rejected-counter tags: - key: xx expression: "2" cache-name: buckets - filter-method: gateway - url: .* - filter-order: -100000 - rate-limits: - - bandwidths: - - capacity: 5 - time: 10 - unit: seconds refill-speed: interval \ No newline at end of file +spring: + cache: + type: hazelcast +management: + endpoints: + web: + exposure: + include: "*" +bucket4j: + default-tags: + - expression: "1" + key: xx + filters: + - metrics: + types: + - consumed-counter + - rejected-counter + tags: + - key: xx + expression: "2" + cache-name: buckets + filter-method: gateway + url: .* + filter-order: -100000 + rate-limits: + - bandwidths: + - capacity: 5 + time: 10 + unit: seconds + refill-speed: interval + - bandwidths: + - capacity: 5 + time: 10 + unit: seconds + refill-speed: interval \ No newline at end of file