1.Retry introduction
Spring Retry
It provides the ability to automatically re-invoke failed operations. This is useful in situations where the error may be temporary (such as a transient network failure). Starting from version 2.2.0, the retry function has been withdrawn from Spring Batch and became a new independent library: Spring Retry
scenes to be used
In the daily development process, it is inevitable that there will be interactions with third-party interfaces, such as text message sending, remote service calls, lock competition, etc. When normal calls are abnormal, such as network jitters, these intermittent abnormalities will occur for a period of time. It will recover on its own after that. In order for the program to be more robust and less prone to failures, business operations need to be retriggered to prevent intermittent exceptions from affecting program performance.
tips:Idempotence
Be careful when using retries in non-idempotent situations. The definition of idempotence in HTTP/1.1 is: one and multiple requests for a resource should have the same result for the resource itself (except for problems such as network timeout). In other words, any multiple executions have the same impact on the resource itself as one execution.
2.Code Project
Experimental goal: simulate three-party interface exceptions and trigger retries
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>SpringRetry</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--retry-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>
</project>
application.yaml
server:
port: 8088
RemoteApiService.java
@Retryable
Note, the value value indicates which exceptions trigger retries, maxAttempts indicates the maximum number of retries, the default is 3, delay indicates the delay time of retry, and multiplier indicates that the last delay time is a multiple of this time.@Recover
Note: when the number of retries reaches the set number, an exception will still be thrown and the callback function will be executed.
package com.et.retry.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
@Service
public class RemoteApiService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
public boolean pay(int num) throws Exception{
logger.info("invoke third method");
logger.info("do something... {}", LocalDateTime.now());
//mock exception
if(num==0) {
throw new Exception("error,need retry");
}
return true;
}
@Recover
public boolean recover(int num) throws Exception {
logger.info("recover ... {},{}", num, LocalDateTime.now());
return false;
}
}
DemoApplication.java
Add the @EnableRetry annotation to the main class to enable the retry mechanism.
package com.et.retry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@EnableRetry
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
The above are just some key codes. For all codes, please see the code repository below.
code repository
3.Test
Below is a test class
package com.et.retry;
import com.et.retry.service.RemoteApiService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoTests {
private Logger log = LoggerFactory.getLogger(getClass());
@Autowired
RemoteApiService remoteApiService;
@Before
public void before() {
log.info("init some data");
}
@After
public void after(){
log.info("clean some data");
}
@Test
public void execute() throws Exception {
log.info("pay result:"+remoteApiService.pay(0));
}
}
Run the test method and the results are as follows
2024-04-03 10:35:54.738 INFO 13096 --- [ main] com.et.retry.DemoTests : init some data
2024-04-03 10:35:54.756 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : invoke third method
2024-04-03 10:35:54.759 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : do something... 2024-04-03T10:35:54.758
2024-04-03 10:35:56.766 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : invoke third method
2024-04-03 10:35:56.766 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : do something... 2024-04-03T10:35:56.766
2024-04-03 10:35:59.776 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : invoke third method
2024-04-03 10:35:59.776 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : do something... 2024-04-03T10:35:59.776
2024-04-03 10:35:59.776 INFO 13096 --- [ main] com.et.retry.service.RemoteApiService : recover ... 0,2024-04-03T10:35:59.776
2024-04-03 10:35:59.776 INFO 13096 --- [ main] com.et.retry.DemoTests : pay result:false
2024-04-03 10:35:59.778 INFO 13096 --- [ main] com.et.retry.DemoTests : clean some data