diff --git a/spring-cloud/spring-cloud-zuul-throttling/pom.xml b/spring-cloud/spring-cloud-zuul-throttling/pom.xml index 180cc96f00..b34ade662a 100644 --- a/spring-cloud/spring-cloud-zuul-throttling/pom.xml +++ b/spring-cloud/spring-cloud-zuul-throttling/pom.xml @@ -37,7 +37,7 @@ com.marcosbarbero.cloud spring-cloud-zuul-ratelimit - LATEST + 2.2.0.RELEASE org.springframework.boot diff --git a/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/ZuulRatelimitDemoApplication.java b/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/ZuulRatelimitDemoApplication.java index 18a779f976..cc13b7a046 100644 --- a/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/ZuulRatelimitDemoApplication.java +++ b/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/ZuulRatelimitDemoApplication.java @@ -1,12 +1,53 @@ package com.baeldung.spring.cloud.zuulratelimitdemo; +import javax.servlet.http.HttpServletRequest; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.zuul.filters.Route; +import org.springframework.context.annotation.Bean; + +import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.RateLimitKeyGenerator; +import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.RateLimitUtils; +import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.properties.RateLimitProperties; +import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.RateLimiterErrorHandler; +import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.DefaultRateLimiterErrorHandler; +import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.DefaultRateLimitKeyGenerator; @SpringBootApplication public class ZuulRatelimitDemoApplication { - public static void main(String[] args) { - SpringApplication.run(ZuulRatelimitDemoApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ZuulRatelimitDemoApplication.class, args); + } + + @Bean + public RateLimitKeyGenerator ratelimitKeyGenerator(RateLimitProperties properties, RateLimitUtils rateLimitUtils) { + return new DefaultRateLimitKeyGenerator(properties, rateLimitUtils) { + @Override + public String key(HttpServletRequest request, Route route, RateLimitProperties.Policy policy) { + return super.key(request, route, policy) + ":" + request.getMethod(); + } + }; + } + + @Bean + public RateLimiterErrorHandler rateLimitErrorHandler() { + return new DefaultRateLimiterErrorHandler() { + @Override + public void handleSaveError(String key, Exception e) { + // custom code + } + + @Override + public void handleFetchError(String key, Exception e) { + // custom code + } + + @Override + public void handleError(String msg, Exception e) { + // custom code + } + }; + } } diff --git a/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingController.java b/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingController.java index 4a27324b4d..f0facc621a 100644 --- a/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingController.java +++ b/spring-cloud/spring-cloud-zuul-throttling/src/main/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingController.java @@ -11,16 +11,16 @@ import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("/greeting") public class GreetingController { - public static final String SIMPLE_RESPONSE = "Hi!"; - public static final String ADVANCED_RESPONSE = "Hello, how you doing?"; + public static final String SIMPLE_RESPONSE = "Hi!"; + public static final String ADVANCED_RESPONSE = "Hello, how you doing?"; - @GetMapping("/simple") - public ResponseEntity serviceA() { - return ResponseEntity.ok(SIMPLE_RESPONSE); - } + @GetMapping("/simple") + public ResponseEntity getSimple() { + return ResponseEntity.ok(SIMPLE_RESPONSE); + } - @GetMapping("/advanced") - public ResponseEntity serviceB() { - return ResponseEntity.ok(ADVANCED_RESPONSE); - } + @GetMapping("/advanced") + public ResponseEntity getAdvanced() { + return ResponseEntity.ok(ADVANCED_RESPONSE); + } } diff --git a/spring-cloud/spring-cloud-zuul-throttling/src/main/resources/application.yml b/spring-cloud/spring-cloud-zuul-throttling/src/main/resources/application.yml index 884ee9446e..86a29ca06d 100644 --- a/spring-cloud/spring-cloud-zuul-throttling/src/main/resources/application.yml +++ b/spring-cloud/spring-cloud-zuul-throttling/src/main/resources/application.yml @@ -11,13 +11,13 @@ zuul: repository: JPA policy-list: serviceSimple: - - limit: 5 - refresh-interval: 60 - type: - - origin + - limit: 5 + refresh-interval: 60 + type: + - origin serviceAdvanced: - - limit: 1 - refresh-interval: 2 - type: - - origin - strip-prefix: true + - limit: 1 + refresh-interval: 2 + type: + - origin + strip-prefix: true \ No newline at end of file diff --git a/spring-cloud/spring-cloud-zuul-throttling/src/test/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingControllerTest.java b/spring-cloud/spring-cloud-zuul-throttling/src/test/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingControllerTest.java index 360d005c43..fe81838d5d 100644 --- a/spring-cloud/spring-cloud-zuul-throttling/src/test/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingControllerTest.java +++ b/spring-cloud/spring-cloud-zuul-throttling/src/test/java/com/baeldung/spring/cloud/zuulratelimitdemo/controller/GreetingControllerTest.java @@ -28,73 +28,86 @@ import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class GreetingControllerTest { - private static final String SIMPLE_GREETING = "/greeting/simple"; - private static final String ADVANCED_GREETING = "/greeting/advanced"; + private static final String SIMPLE_GREETING = "/greeting/simple"; + private static final String ADVANCED_GREETING = "/greeting/advanced"; - @Autowired - private TestRestTemplate restTemplate; + @Autowired + private TestRestTemplate restTemplate; - @Test - public void whenRequestNotExceedingCapacity_thenReturnOkResponse() { - ResponseEntity response = this.restTemplate.getForEntity(SIMPLE_GREETING, String.class); - HttpHeaders headers = response.getHeaders(); - String key = "rate-limit-application_serviceSimple_127.0.0.1"; - assertHeaders(headers, key, false, false); - assertEquals(OK, response.getStatusCode()); - } + @Test + public void whenRequestNotExceedingCapacity_thenReturnOkResponse() { + ResponseEntity response = this.restTemplate.getForEntity(SIMPLE_GREETING, String.class); + HttpHeaders headers = response.getHeaders(); + String key = "rate-limit-application_serviceSimple_127.0.0.1"; - @Test - public void whenRequestExceedingCapacity_thenReturnTooManyRequestsResponse() throws InterruptedException { - ResponseEntity response = this.restTemplate - .getForEntity(ADVANCED_GREETING, String.class); - HttpHeaders headers = response.getHeaders(); - String key = "rate-limit-application_serviceAdvanced_127.0.0.1"; - assertHeaders(headers, key, false, false); - assertEquals(OK, response.getStatusCode()); + String limit = headers.getFirst(HEADER_LIMIT + key); + String remaining = headers.getFirst(HEADER_REMAINING + key); + String reset = headers.getFirst(HEADER_RESET + key); - for (int i = 0; i < 2; i++) { - response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class); + assertEquals(limit, "5"); + assertEquals(remaining, "4"); + assertEquals(reset, "60000"); + + assertEquals(OK, response.getStatusCode()); } - assertEquals(TOO_MANY_REQUESTS, response.getStatusCode()); - assertNotEquals(GreetingController.ADVANCED_RESPONSE, response.getBody()); + @Test + public void whenRequestExceedingCapacity_thenReturnTooManyRequestsResponse() throws InterruptedException { + ResponseEntity response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class); + HttpHeaders headers = response.getHeaders(); + String key = "rate-limit-application_serviceAdvanced_127.0.0.1"; + assertHeaders(headers, key, false, false); + assertEquals(OK, response.getStatusCode()); - TimeUnit.SECONDS.sleep(2); + for (int i = 0; i < 2; i++) { + response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class); + } - response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class); - headers = response.getHeaders(); - assertHeaders(headers, key, false, false); - assertEquals(OK, response.getStatusCode()); - } + headers = response.getHeaders(); + String limit = headers.getFirst(HEADER_LIMIT + key); + String remaining = headers.getFirst(HEADER_REMAINING + key); + String reset = headers.getFirst(HEADER_RESET + key); - private void assertHeaders(HttpHeaders headers, String key, boolean nullable, - boolean quotaHeaders) { - String quota = headers.getFirst(HEADER_QUOTA + key); - String remainingQuota = headers.getFirst(HEADER_REMAINING_QUOTA + key); - String limit = headers.getFirst(HEADER_LIMIT + key); - String remaining = headers.getFirst(HEADER_REMAINING + key); - String reset = headers.getFirst(HEADER_RESET + key); + assertEquals(limit, "1"); + assertEquals(remaining, "0"); + assertNotEquals(reset, "2000"); - if (nullable) { - if (quotaHeaders) { - assertNull(quota); - assertNull(remainingQuota); - } else { - assertNull(limit); - assertNull(remaining); - } - assertNull(reset); - } else { - if (quotaHeaders) { - assertNotNull(quota); - assertNotNull(remainingQuota); - } else { - assertNotNull(limit); - assertNotNull(remaining); - } - assertNotNull(reset); + assertEquals(TOO_MANY_REQUESTS, response.getStatusCode()); + assertNotEquals(GreetingController.ADVANCED_RESPONSE, response.getBody()); + + TimeUnit.SECONDS.sleep(2); + + response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class); + headers = response.getHeaders(); + assertHeaders(headers, key, false, false); + assertEquals(OK, response.getStatusCode()); } - } + private void assertHeaders(HttpHeaders headers, String key, boolean nullable, boolean quotaHeaders) { + String quota = headers.getFirst(HEADER_QUOTA + key); + String remainingQuota = headers.getFirst(HEADER_REMAINING_QUOTA + key); + String limit = headers.getFirst(HEADER_LIMIT + key); + String remaining = headers.getFirst(HEADER_REMAINING + key); + String reset = headers.getFirst(HEADER_RESET + key); + if (nullable) { + if (quotaHeaders) { + assertNull(quota); + assertNull(remainingQuota); + } else { + assertNull(limit); + assertNull(remaining); + } + assertNull(reset); + } else { + if (quotaHeaders) { + assertNotNull(quota); + assertNotNull(remainingQuota); + } else { + assertNotNull(limit); + assertNotNull(remaining); + } + assertNotNull(reset); + } + } }