This post talks about a better way to retry failed operations in Java using classes in Spring.
Requirements
Stuff used in this post.
- Spring Core 4.2.1.RELEASE
Retry Codes in Vanilla Java
Off the top of our head, how would we code something that does the following?
- retries a failed operation n times
- each retry, the amount of time to wait before next try increases
- stops retrying an operation after n times
[wp_ad_camp_5]
Here’s mine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public static void main(String[] args) throws InterruptedException { int stopAfter = 5; int waitTime = 1000; // Increase waiting time after every try double subsequentIncreaseInWaitFactor = 2.5; for(int i = 0; i < stopAfter; i++) { System.out.println(LocalTime.now() + " | Wait time(ms): " + waitTime); // always returns false if(mockedGetConnection()) { break; } TimeUnit.MILLISECONDS.sleep(waitTime); waitTime*=subsequentIncreaseInWaitFactor; } System.out.println("Retries done!"); } |
Output:
1 2 3 4 5 6 | 01:13:00.135 | Wait time(ms): 1000 01:13:01.137 | Wait time(ms): 2500 01:13:03.637 | Wait time(ms): 6250 01:13:09.887 | Wait time(ms): 15625 01:13:25.513 | Wait time(ms): 39062 Retries done! |
That’s ugly!
Let’s try a better way at doing this.
Back Off
[wp_ad_camp_4]
Spring Core has these related interfaces and classes:
- Interfaces
- Classes
- org.springframework.util.backoff.ExponentialBackOff
- Implementation of BackOff that increases the back off period for each retry attempt. When the interval has reached the max interval, it is no longer increased. Stops retrying once the max elapsed time has been reached.
- org.springframework.util.backoff.FixedBackOff
- A simple BackOff implementation that provides a fixed interval between two attempts and a maximum number of retries.
- org.springframework.util.backoff.ExponentialBackOff
They allow us to create the wait-and-retry functionality in a more elegant way.
Using FixedBackOff
These are sample codes for FixedBackOff.
[wp_ad_camp_3]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public static void main(String[] args) throws InterruptedException { FixedBackOff fixedBackOff = new FixedBackOff(); fixedBackOff.setInterval(1000); fixedBackOff.setMaxAttempts(5); BackOffExecution exec = fixedBackOff.start(); long waitTime; while((waitTime = exec.nextBackOff()) != BackOffExecution.STOP) { System.out.println(LocalTime.now() + " | Wait time(ms): " + waitTime); // Always false if(mockedGetConnection()) { break; } TimeUnit.MILLISECONDS.sleep(waitTime); } } |
Output:
1 2 3 4 5 | 01:11:37.056 | Wait time(ms): 1000 01:11:38.058 | Wait time(ms): 1000 01:11:39.058 | Wait time(ms): 1000 01:11:40.058 | Wait time(ms): 1000 01:11:41.059 | Wait time(ms): 1000 |
Using ExponentialBackOff
These are codes for ExponentialBackOff.
[wp_ad_camp_2]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public static void main(String[] args) throws InterruptedException { ExponentialBackOff exponentialBackOff = new ExponentialBackOff(); exponentialBackOff.setMultiplier(2.5); exponentialBackOff.setInitialInterval(1000); // Max waiting time exponentialBackOff.setMaxElapsedTime(12000); BackOffExecution exec = exponentialBackOff.start(); long waitTime; while((waitTime = exec.nextBackOff()) != BackOffExecution.STOP) { System.out.println(LocalTime.now() + " | Wait time(ms): " + waitTime); // Always false if(mockedGetConnection()) { break; } TimeUnit.MILLISECONDS.sleep(waitTime); } } |
Output:
1 2 3 4 | 01:07:20.551 | Wait time(ms): 1000 01:07:21.553 | Wait time(ms): 2500 01:07:24.053 | Wait time(ms): 6250 01:07:30.303 | Wait time(ms): 15625 |
[wp_ad_camp_1]