Skip to content

Commit 37950f1

Browse files
authored
Merge pull request thombergs#123 from saajn/spring-resilience
Resilience4j TimeLimiter article
2 parents e4d52aa + c793758 commit 37950f1

File tree

6 files changed

+373
-0
lines changed

6 files changed

+373
-0
lines changed

resilience4j/springboot-resilience4j/README.md

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

77
* [Implementing Retry with Spring_Boot_Resilience4j](https://reflectoring.io/retry-with-springboot-resilience4j/)
88
* [Implementing Rate Limiting with Spring_Boot_Resilience4j](https://reflectoring.io/rate-limiting-with-springboot-resilience4j/)
9+
* [Implementing Time Limiting with Spring_Boot_Resilience4j](https://reflectoring.io/time-limiting-with-springboot-resilience4j/)

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
@@ -14,6 +14,9 @@ public class SpringbootResilience4jApplication {
1414
@Autowired
1515
private RateLimiterExamplesRunner rateLimiterExamplesRunner;
1616

17+
@Autowired
18+
private TimeLimiterExamplesRunner timeLimiterExamplesRunner;
19+
1720
public static void main(String[] args) {
1821
SpringApplication.run(SpringbootResilience4jApplication.class, args);
1922
}
@@ -22,5 +25,6 @@ public static void main(String[] args) {
2225
public void runExamples() {
2326
retryExamplesRunner.run();
2427
rateLimiterExamplesRunner.run();
28+
timeLimiterExamplesRunner.run();
2529
}
2630
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package io.reflectoring.resilience4j.springboot;
2+
3+
import io.reflectoring.resilience4j.springboot.model.Flight;
4+
import io.reflectoring.resilience4j.springboot.model.SearchRequest;
5+
import java.time.LocalDateTime;
6+
import java.time.format.DateTimeFormatter;
7+
import java.util.List;
8+
import java.util.concurrent.CompletableFuture;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.stereotype.Component;
11+
12+
@Component
13+
public class TimeLimiterExamplesRunner {
14+
15+
@Autowired
16+
private TimeLimitingService service;
17+
18+
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss SSS");
19+
20+
public static void main(String[] args) {
21+
TimeLimiterExamplesRunner runner = new TimeLimiterExamplesRunner();
22+
runner.run();
23+
}
24+
25+
static void delay(int seconds) {
26+
// sleep to simulate delay
27+
try {
28+
Thread.sleep(seconds * 1000);
29+
} catch (InterruptedException e) {
30+
e.printStackTrace();
31+
}
32+
}
33+
34+
public void run() {
35+
System.out.println("Running timelimiter examples");
36+
37+
System.out.println(
38+
"----------------------- basicExample ----------------------------------------------------");
39+
basicExample();
40+
41+
delay(2); // delay just to let the above async operation to complete
42+
43+
System.out.println(
44+
"----------------------------------------------------------------------------------------------------");
45+
46+
System.out.println("----------------------- timeoutExample ----------------------------------------------");
47+
timeoutExample();
48+
49+
delay(2); // delay just to let the above async operation to complete
50+
51+
System.out.println("----------------------------------------------------------------------------------------------------");
52+
53+
System.out.println("----------------------- fallbackExample ----------------------------------------------");
54+
fallbackExample();
55+
56+
delay(2); // delay just to let the above async operation to complete
57+
58+
System.out.println("----------------------------------------------------------------------------------------------------");
59+
60+
System.out.println(
61+
"----------------------- eventsExample ----------------------------------------------------");
62+
eventsExample();
63+
delay(10); // delay just to let the above async operation to complete
64+
System.out.println(
65+
"----------------------------------------------------------------------------------------------------");
66+
}
67+
68+
private void eventsExample() {
69+
SearchRequest request = new SearchRequest("NYC", "LAX", "10/30/2021");
70+
for (int i = 0; i < 10; i++) {
71+
int attempt = i;
72+
service.eventsExample(request)
73+
.whenComplete((r, t) -> {
74+
if (t != null) {
75+
System.out.println("Error occurred on search " + attempt + ": " + t.getMessage());
76+
}
77+
if (r != null) {
78+
System.out
79+
.println("Search " + attempt + " successful, found " + r.size() + " flights");
80+
}
81+
});
82+
}
83+
}
84+
85+
private void timeoutExample() {
86+
SearchRequest request = new SearchRequest("NYC", "LAX", "10/30/2021");
87+
System.out.println("Calling search; current thread = " + Thread.currentThread().getName());
88+
CompletableFuture<List<Flight>> results = service.timeoutExample(request);
89+
results.whenComplete((result, ex) -> {
90+
if (ex != null) {
91+
System.out.println("Exception " +
92+
ex.getMessage() +
93+
" on thread " +
94+
Thread.currentThread().getName() +
95+
" at " +
96+
LocalDateTime.now().format(formatter));
97+
ex.printStackTrace();
98+
}
99+
if (result != null) {
100+
System.out.println(result + " on thread " + Thread.currentThread().getName());
101+
}
102+
});
103+
}
104+
105+
private void basicExample() {
106+
SearchRequest request = new SearchRequest("NYC", "LAX", "10/30/2021");
107+
System.out.println("Calling search; current thread = " + Thread.currentThread().getName());
108+
CompletableFuture<List<Flight>> results = service.basicExample(request);
109+
results.whenComplete((result, ex) -> {
110+
if (ex != null) {
111+
System.out.println("Exception " +
112+
ex.getMessage() +
113+
" on thread " +
114+
Thread.currentThread().getName() +
115+
" at " +
116+
LocalDateTime.now().format(formatter));
117+
}
118+
if (result != null) {
119+
System.out.println(result + " on thread " + Thread.currentThread().getName());
120+
}
121+
});
122+
}
123+
124+
private void fallbackExample() {
125+
SearchRequest request = new SearchRequest("NYC", "LAX", "10/30/2021");
126+
System.out.println("Calling search; current thread = " + Thread.currentThread().getName());
127+
CompletableFuture<List<Flight>> results = service.fallbackExample(request);
128+
results.whenComplete((result, ex) -> {
129+
if (ex != null) {
130+
System.out.println("Exception " +
131+
ex.getMessage() +
132+
" on thread " +
133+
Thread.currentThread().getName() +
134+
" at " +
135+
LocalDateTime.now().format(formatter));
136+
}
137+
if (result != null) {
138+
System.out.println(result + " on thread " + Thread.currentThread().getName());
139+
}
140+
});
141+
}
142+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package io.reflectoring.resilience4j.springboot;
2+
3+
import io.github.resilience4j.micrometer.tagged.TaggedTimeLimiterMetrics;
4+
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
5+
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
6+
import io.github.resilience4j.timelimiter.TimeLimiter.EventPublisher;
7+
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
8+
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
9+
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
10+
import io.micrometer.core.instrument.Measurement;
11+
import io.micrometer.core.instrument.Meter;
12+
import io.micrometer.core.instrument.MeterRegistry;
13+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
14+
import io.reflectoring.resilience4j.springboot.model.Flight;
15+
import io.reflectoring.resilience4j.springboot.model.SearchRequest;
16+
import io.reflectoring.resilience4j.springboot.services.FlightSearchService;
17+
import java.sql.Time;
18+
import java.time.Duration;
19+
import java.time.LocalDateTime;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
22+
import java.util.List;
23+
import java.util.concurrent.CompletableFuture;
24+
import java.util.concurrent.CompletionStage;
25+
import java.util.concurrent.ExecutionException;
26+
import java.util.concurrent.Executors;
27+
import java.util.concurrent.ScheduledExecutorService;
28+
import java.util.concurrent.TimeUnit;
29+
import java.util.concurrent.TimeoutException;
30+
import java.util.function.Consumer;
31+
import java.util.function.Supplier;
32+
import java.util.stream.StreamSupport;
33+
import javax.annotation.PostConstruct;
34+
import org.springframework.beans.factory.annotation.Autowired;
35+
import org.springframework.stereotype.Service;
36+
37+
@Service
38+
public class TimeLimitingService {
39+
@Autowired
40+
private FlightSearchService remoteSearchService;
41+
42+
@Autowired
43+
private TimeLimiterRegistry timeLimiterRegistry;
44+
45+
/*
46+
void printDefaultValues() {
47+
TimeLimiterConfig config = TimeLimiterConfig.ofDefaults();
48+
49+
System.out.println(
50+
"getTimeoutDuration in ms = " + Duration.from(config.getTimeoutDuration()).toMillis());
51+
System.out.println("shouldCancelRunningFuture = " + config.shouldCancelRunningFuture());
52+
} */
53+
54+
55+
@TimeLimiter(name = "basicExample")
56+
CompletableFuture<List<Flight>> basicExample(SearchRequest request) {
57+
return CompletableFuture.supplyAsync(() -> remoteSearchService.searchFlightsTakingOneSecond(request));
58+
}
59+
60+
@TimeLimiter(name = "timeoutExample")
61+
CompletableFuture<List<Flight>> timeoutExample(SearchRequest request) {
62+
return CompletableFuture.supplyAsync(() -> remoteSearchService.searchFlightsTakingOneSecond(request));
63+
}
64+
65+
@TimeLimiter(name = "timeAndRateLimiter")
66+
@RateLimiter(name = "timeAndRateLimiter")
67+
CompletableFuture<List<Flight>> aspectOrderExample(SearchRequest request) {
68+
return CompletableFuture.supplyAsync(() -> remoteSearchService.searchFlightsTakingOneSecond(request));
69+
}
70+
71+
/*
72+
void basicExample_ExcecuteCompletionStage() {
73+
TimeLimiterConfig config = TimeLimiterConfig.custom()
74+
.timeoutDuration(Duration.ofMillis(500))
75+
.build();
76+
77+
TimeLimiterRegistry registry = TimeLimiterRegistry.of(config);
78+
TimeLimiter limiter = registry.timeLimiter("flightSearch");
79+
80+
FlightSearchService service = new FlightSearchService();
81+
SearchRequest request = new SearchRequest("NYC", "LAX", "08/30/2020");
82+
83+
Supplier<List<Flight>> flightSupplier = () -> service.searchFlightsTakingOneSecond(request);
84+
Supplier<CompletionStage<List<Flight>>> origCompletionStageSupplier = () -> CompletableFuture
85+
.supplyAsync(flightSupplier);
86+
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
87+
CompletionStage<List<Flight>> decoratedCompletionStage = limiter
88+
.executeCompletionStage(scheduler, origCompletionStageSupplier);
89+
90+
decoratedCompletionStage.whenComplete((result, ex) -> {
91+
if (ex != null) {
92+
System.out.println("Exception " +
93+
ex.getMessage() +
94+
" on thread " +
95+
Thread.currentThread().getName() +
96+
" at " +
97+
LocalDateTime.now().format(formatter));
98+
}
99+
if (result != null) {
100+
System.out.println(result + " on thread " + Thread.currentThread().getName());
101+
}
102+
});
103+
104+
scheduler.shutdown();
105+
}
106+
107+
108+
void whenToUseExample() {
109+
CompletableFuture.supplyAsync(this::slowMethod).thenAccept(System.out::println);
110+
}
111+
112+
void whenToUseExample_Blocking()
113+
throws InterruptedException, ExecutionException, TimeoutException {
114+
CompletableFuture<Integer> completableFuture = CompletableFuture
115+
.supplyAsync(this::slowMethod);
116+
Integer result = completableFuture.get(3000, TimeUnit.MILLISECONDS);
117+
System.out.println(result);
118+
}
119+
120+
int slowMethod() {
121+
System.out.println(Thread.currentThread().getName());
122+
// sleep to simulate delay
123+
try {
124+
Thread.sleep(2000);
125+
} catch (InterruptedException e) {
126+
e.printStackTrace();
127+
}
128+
return 0;
129+
}
130+
131+
static void delay(int seconds) {
132+
// sleep to simulate delay
133+
try {
134+
Thread.sleep(seconds * 1000);
135+
} catch (InterruptedException e) {
136+
e.printStackTrace();
137+
}
138+
} */
139+
140+
@TimeLimiter(name = "eventsExample")
141+
CompletableFuture<List<Flight>> eventsExample(SearchRequest request) {
142+
return CompletableFuture.supplyAsync(() -> remoteSearchService.searchFlightsTakingRandomTime(request));
143+
}
144+
145+
@TimeLimiter(name = "fallbackExample", fallbackMethod = "localCacheFlightSearch")
146+
CompletableFuture<List<Flight>> fallbackExample(SearchRequest request) {
147+
return CompletableFuture.supplyAsync(() -> remoteSearchService.searchFlightsTakingOneSecond(request));
148+
}
149+
150+
private CompletableFuture<List<Flight>> localCacheFlightSearch(SearchRequest request, TimeoutException rnp) {
151+
System.out.println("Returning search results from cache");
152+
System.out.println(rnp.getMessage());
153+
CompletableFuture<List<Flight>> result = new CompletableFuture<>();
154+
result.complete(Arrays.asList(
155+
new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()),
156+
new Flight("XY 781", request.getFlightDate(), request.getFrom(), request.getTo())));
157+
return result;
158+
}
159+
160+
@PostConstruct
161+
void postConstruct() {
162+
EventPublisher eventPublisher = timeLimiterRegistry.timeLimiter("eventsExample").getEventPublisher();
163+
eventPublisher.onSuccess(System.out::println);
164+
eventPublisher.onError(System.out::println);
165+
eventPublisher.onTimeout(System.out::println);
166+
}
167+
}

resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/FlightSearchService.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
import java.util.Arrays;
1414
import java.util.Collections;
1515
import java.util.List;
16+
import java.util.Random;
1617
import org.springframework.stereotype.Service;
1718

1819
@Service
1920
public class FlightSearchService {
2021

2122
PotentialFailure potentialFailure = new NoFailure();
2223
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss SSS");
24+
Random random = new Random();
2325

2426
PotentialFailureCheckedException potentialFailureCheckedException = new NoCheckedExceptionFailure();
2527

@@ -108,4 +110,42 @@ public SearchResponse httpSearchFlights(SearchRequest request) throws IOExceptio
108110
response.setFlights(flights);
109111
return response;
110112
}
113+
114+
public List<Flight> searchFlightsTakingOneSecond(SearchRequest request) {
115+
System.out.println("Searching for flights; "
116+
+ "current time = " + LocalDateTime.now().format(formatter) +
117+
"; current thread = " + Thread.currentThread().getName());
118+
119+
try {
120+
Thread.sleep(1000);
121+
} catch (InterruptedException e) {
122+
e.printStackTrace();
123+
}
124+
125+
List<Flight> flights = Arrays.asList(
126+
new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()),
127+
new Flight("XY 746", request.getFlightDate(), request.getFrom(), request.getTo())
128+
);
129+
System.out.println("Flight search successful at " + LocalDateTime.now().format(formatter));
130+
return flights;
131+
}
132+
133+
public List<Flight> searchFlightsTakingRandomTime(SearchRequest request) {
134+
long delay = random.nextInt(3000);
135+
try {
136+
Thread.sleep(delay);
137+
} catch (InterruptedException e) {
138+
e.printStackTrace();
139+
}
140+
System.out.println("Searching for flights; "
141+
+ "current time = " + LocalDateTime.now().format(formatter) +
142+
"; current thread = " + Thread.currentThread().getName());
143+
144+
List<Flight> flights = Arrays.asList(
145+
new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()),
146+
new Flight("XY 746", request.getFlightDate(), request.getFrom(), request.getTo())
147+
);
148+
System.out.println("Flight search successful");
149+
return flights;
150+
}
111151
}

0 commit comments

Comments
 (0)