Skip to content

Commit 83e5e10

Browse files
authored
Merge pull request thombergs#111 from saajn/spring-resilience
Code for Spring Boot Resilience4j RateLimiter article
2 parents b7139e9 + 28147ad commit 83e5e10

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed

resilience4j/springboot-resilience4j/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ Run the SpringbootResilience4jApplication program
55
## Blog posts
66

77
* [Implementing Retry with Spring_Boot_Resilience4j](https://reflectoring.io/retry-with-springboot-resilience4j/)
8+
* [Implementing Rate Limiting with Spring_Boot_Resilience4j](https://reflectoring.io/rate-limiting-with-springboot-resilience4j/)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package io.reflectoring.resilience4j.springboot;
2+
3+
import io.reflectoring.resilience4j.springboot.model.SearchRequest;
4+
import java.time.Duration;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.stereotype.Component;
7+
8+
@Component
9+
public class RateLimiterExamplesRunner {
10+
11+
@Autowired
12+
private RateLimitingService service;
13+
14+
public static void main(String[] args) {
15+
RateLimiterExamplesRunner runner = new RateLimiterExamplesRunner();
16+
runner.run();
17+
}
18+
19+
public void run() {
20+
System.out.println("Running ratelimiter examples");
21+
22+
System.out.println("----------------------------- basicExample ------------------------------------------");
23+
basicExample();
24+
System.out.println("-----------------------------------------------------------------------");
25+
26+
System.out.println("----------------------------- timeoutExample ------------------------------------------");
27+
timeoutExample();
28+
System.out.println("-----------------------------------------------------------------------");
29+
30+
System.out.println("------------------------------ multipleLimits_2rps_40rpm_sequential -----------------------------------------");
31+
multipleLimits_2rps_40rpm_sequential();
32+
System.out.println("-----------------------------------------------------------------------");
33+
34+
System.out.println("------------------------------- changeLimitsExample ----------------------------------------");
35+
changeLimitsExample();
36+
System.out.println("-----------------------------------------------------------------------");
37+
38+
System.out.println("------------------------------- retryAndRateLimit ----------------------------------------");
39+
retryAndRateLimit();
40+
System.out.println("-----------------------------------------------------------------------");
41+
42+
System.out.println("------------------------------ rateLimiterEvents -----------------------------------------");
43+
rateLimiterEvents();
44+
System.out.println("-----------------------------------------------------------------------");
45+
46+
System.out.println("----------------------------- fallbackExample ------------------------------------------");
47+
fallbackExample();
48+
System.out.println("-----------------------------------------------------------------------");
49+
}
50+
51+
private void rateLimiterEvents() {
52+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
53+
54+
try {
55+
System.out.println(service.rateLimiterEventsExample(request));
56+
System.out.println(service.rateLimiterEventsExample(request));
57+
}
58+
catch (Exception e) {
59+
System.out.println(e.getMessage());
60+
}
61+
}
62+
63+
private void retryAndRateLimit() {
64+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
65+
66+
System.out.println(service.retryAndRateLimit(request));
67+
System.out.println(service.retryAndRateLimit(request));
68+
}
69+
70+
private void changeLimitsExample() {
71+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
72+
73+
for (int i=0; i<6; i++) {
74+
System.out.println(service.changeLimitsExample(request));
75+
}
76+
77+
try {
78+
Thread.sleep(2000);
79+
} catch (InterruptedException e) {
80+
e.printStackTrace();
81+
}
82+
83+
service.updateRateLimits("changeLimitsExample", 2, Duration.ofSeconds(2));
84+
System.out.println("Rate limits changed");
85+
86+
for (int i=0; i<6; i++) {
87+
System.out.println(service.changeLimitsExample(request));
88+
}
89+
}
90+
91+
private void multipleLimits_2rps_40rpm_sequential() {
92+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
93+
for (int i=0; i<45; i++) {
94+
try {
95+
System.out.println(service.multipleRateLimitsExample(request));
96+
}
97+
catch (Exception e) {
98+
e.printStackTrace();
99+
}
100+
}
101+
}
102+
103+
private void timeoutExample() {
104+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
105+
try {
106+
for (int i=0; i<3; i++) {
107+
System.out.println(service.timeoutExample(request));
108+
}
109+
}
110+
catch (Exception e) {
111+
e.printStackTrace();
112+
}
113+
}
114+
115+
private void basicExample() {
116+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
117+
for (int i=0; i<4; i++) {
118+
try {
119+
System.out.println(service.basicExample(request));
120+
} catch (Exception e) {
121+
e.printStackTrace();
122+
}
123+
}
124+
}
125+
126+
private void fallbackExample() {
127+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021");
128+
System.out.println(service.fallbackExample(request));
129+
System.out.println(service.fallbackExample(request));
130+
}
131+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package io.reflectoring.resilience4j.springboot;
2+
3+
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
4+
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
5+
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
6+
import io.github.resilience4j.retry.RetryRegistry;
7+
import io.github.resilience4j.retry.annotation.Retry;
8+
import io.reflectoring.resilience4j.springboot.model.Flight;
9+
import io.reflectoring.resilience4j.springboot.model.SearchRequest;
10+
import io.reflectoring.resilience4j.springboot.services.FlightSearchService;
11+
import java.time.Duration;
12+
import java.time.format.DateTimeFormatter;
13+
import java.util.Arrays;
14+
import java.util.List;
15+
import javax.annotation.PostConstruct;
16+
import org.springframework.beans.factory.annotation.Autowired;
17+
import org.springframework.stereotype.Component;
18+
import org.springframework.stereotype.Service;
19+
20+
@Service
21+
public class RateLimitingService {
22+
@Autowired
23+
private FlightSearchService remoteSearchService;
24+
25+
@Autowired
26+
private RPMRateLimitedFlightSearchSearch rpmRateLimitedFlightSearchSearch;
27+
28+
@Autowired
29+
private RateLimiterRegistry registry;
30+
31+
@Autowired
32+
private RetryRegistry retryRegistry;
33+
34+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss SSS");
35+
36+
@RateLimiter(name = "basicExample")
37+
List<Flight> basicExample(SearchRequest request) {
38+
return remoteSearchService.searchFlights(request);
39+
}
40+
41+
@RateLimiter(name = "timeoutExample")
42+
List<Flight> timeoutExample(SearchRequest request) {
43+
return remoteSearchService.searchFlights(request);
44+
}
45+
46+
@RateLimiter(name = "multipleRateLimiters_rps_limiter")
47+
List<Flight> multipleRateLimitsExample(SearchRequest request) {
48+
return rpmRateLimitedFlightSearchSearch.searchFlights(request, remoteSearchService);
49+
}
50+
51+
// doesn't work - @RateLimiter is not a repeatable annotation
52+
// @RateLimiter(name = "multipleRateLimiters_rps_limiter")
53+
// @RateLimiter(name = "multipleRateLimiters_rpm_limiter")
54+
// List<Flight> multipleRateLimitsExample(SearchRequest request) {
55+
// return remoteSearchService.searchFlights(request, remoteSearchService);
56+
// }
57+
58+
// doesn't work - calls within a Spring bean don't go thru the Spring proxy
59+
// @RateLimiter(name = "multipleRateLimiters_rps_limiter")
60+
// List<Flight> rpsLimitedSearch(SearchRequest request) {
61+
// return rpmLimitedSearch(request, remoteSearchService);
62+
// }
63+
64+
// @RateLimiter(name = "multipleRateLimiters_rpm_limiter")
65+
// List<Flight> rpmLimitedSearch(SearchRequest request) {
66+
// return remoteSearchService.searchFlights(request, remoteSearchService);
67+
// }
68+
69+
70+
@RateLimiter(name = "changeLimitsExample")
71+
public List<Flight> changeLimitsExample(SearchRequest request) {
72+
return remoteSearchService.searchFlights(request);
73+
}
74+
75+
@Retry(name = "retryAndRateLimitExample")
76+
@RateLimiter(name = "retryAndRateLimitExample")
77+
public List<Flight> retryAndRateLimit(SearchRequest request) {
78+
return remoteSearchService.searchFlights(request);
79+
}
80+
81+
@RateLimiter(name = "rateLimiterEventsExample")
82+
public List<Flight> rateLimiterEventsExample(SearchRequest request) {
83+
return remoteSearchService.searchFlights(request);
84+
}
85+
86+
public void updateRateLimits(String rateLimiterName, int newLimitForPeriod, Duration newTimeoutDuration) {
87+
io.github.resilience4j.ratelimiter.RateLimiter limiter = registry.rateLimiter(rateLimiterName);
88+
limiter.changeLimitForPeriod(newLimitForPeriod);
89+
limiter.changeTimeoutDuration(newTimeoutDuration);
90+
}
91+
92+
@RateLimiter(name = "fallbackExample", fallbackMethod = "localCacheFlightSearch")
93+
public List<Flight> fallbackExample(SearchRequest request) {
94+
return remoteSearchService.searchFlights(request);
95+
}
96+
97+
private List<Flight> localCacheFlightSearch(SearchRequest request, RequestNotPermitted rnp) {
98+
System.out.println("Returning search results from cache");
99+
return Arrays.asList(
100+
new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()),
101+
new Flight("XY 781", request.getFlightDate(), request.getFrom(), request.getTo()));
102+
}
103+
104+
@PostConstruct
105+
public void postConstruct() {
106+
io.github.resilience4j.retry.Retry.EventPublisher retryEventPublisher = retryRegistry
107+
.retry("retryAndRateLimitExample")
108+
.getEventPublisher();
109+
110+
retryEventPublisher.onRetry(System.out::println);
111+
retryEventPublisher.onSuccess(System.out::println);
112+
113+
io.github.resilience4j.ratelimiter.RateLimiter.EventPublisher eventPublisher = registry
114+
.rateLimiter("rateLimiterEventsExample")
115+
.getEventPublisher();
116+
117+
eventPublisher.onSuccess(System.out::println);
118+
eventPublisher.onFailure(System.out::println);
119+
}
120+
}
121+
122+
@Component
123+
class RPMRateLimitedFlightSearchSearch {
124+
@RateLimiter(name = "multipleRateLimiters_rpm_limiter")
125+
List<Flight> searchFlights(SearchRequest request, FlightSearchService remoteSearchService) {
126+
return remoteSearchService.searchFlights(request);
127+
}
128+
}

resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/SpringbootResilience4jApplication.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@ public class SpringbootResilience4jApplication {
1111
@Autowired
1212
private RetryExamplesRunner retryExamplesRunner;
1313

14+
@Autowired
15+
private RateLimiterExamplesRunner rateLimiterExamplesRunner;
16+
1417
public static void main(String[] args) {
1518
SpringApplication.run(SpringbootResilience4jApplication.class, args);
1619
}
1720

1821
@EventListener(ApplicationReadyEvent.class)
1922
public void runExamples() {
2023
retryExamplesRunner.run();
24+
rateLimiterExamplesRunner.run();
2125
}
2226
}

resilience4j/springboot-resilience4j/src/main/resources/application.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,61 @@ resilience4j:
4444
- java.lang.Exception
4545
waitDuration: 2s
4646

47+
# Retry object used in RateLimitingService.retryAndRateLimitExample()
48+
retryAndRateLimitExample:
49+
maxRetryAttempts: 2
50+
waitDuration: 1s
51+
52+
ratelimiter:
53+
instances:
54+
55+
# RateLimiter object used in RateLimitingService.basicExample()
56+
basicExample:
57+
limitForPeriod: 1
58+
limitRefreshPeriod: 1s
59+
timeoutDuration: 1s
60+
61+
# RateLimiter object used in RateLimitingService.timeoutExample()
62+
timeoutExample:
63+
limitForPeriod: 1
64+
limitRefreshPeriod: 1s
65+
timeoutDuration: 250ms
66+
67+
# RateLimiter object used in RateLimitingService.multipleRateLimitsExample()
68+
multipleRateLimiters_rps_limiter:
69+
limitForPeriod: 2
70+
limitRefreshPeriod: 1s
71+
timeoutDuration: 2s
72+
73+
multipleRateLimiters_rpm_limiter:
74+
limitForPeriod: 40
75+
limitRefreshPeriod: 1m
76+
timeoutDuration: 2s
77+
78+
# RateLimiter object used in RateLimitingService.changeLimitsExample()
79+
changeLimitsExample:
80+
limitForPeriod: 1
81+
limitRefreshPeriod: 1s
82+
timeoutDuration: 1s
83+
84+
# RateLimiter object used in RateLimitingService.retryAndRateLimitExample()
85+
retryAndRateLimitExample:
86+
limitForPeriod: 1
87+
limitRefreshPeriod: 1s
88+
timeoutDuration: 250ms
89+
90+
# RateLimiter object used in RateLimitingService.rateLimiterEventsExample()
91+
rateLimiterEventsExample:
92+
limitForPeriod: 1
93+
limitRefreshPeriod: 1s
94+
timeoutDuration: 50ms
95+
96+
# RateLimiter object used in RateLimitingService.fallbackExample()
97+
fallbackExample:
98+
limitForPeriod: 1
99+
limitRefreshPeriod: 1s
100+
timeoutDuration: 500ms
101+
47102
management:
48103
endpoints:
49104
web:

0 commit comments

Comments
 (0)