Рассмотрим механизм повторных попыток в Spring Boot с аннотациями @Retryable и @Recovery.
Что такое «механизм повторных попыток»?
Это типовой механизм обработки временных сбоев в распределенных системах. А временный сбой — это временная ошибка, которая устраняется повторением операции. Механизмом повторных попыток неудачная операция повторяется, пока она не выполнится или не достигнуто максимальное число попыток.
Этот механизм применяется в следующих сценариях.
- Сбоя сети или связи: взаимодействие приложения с внешними службами или базами данных по сети чревато сетевыми проблемами или недоступностью служб, при сетевых сбоях операция этим механизмом повторяется.
- Операции с базами данных: при работе с БД случаются взаимоблокировки.
- Обработки пользовательских исключений: специфических исключений, повторяемых при определенных условиях.
Реализация в Spring Boot
Зависимости
Версия Java: 17
Версия Spring: 3.1.5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.11</version>
</dependency>
1) RetryableApplication
@SpringBootApplication
@EnableRetry
public class RetryableApplication {
public static void main(String[] args) {
SpringApplication.run(RetryableApplication.class, args);
}
}
@EnableRetry
: этой аннотацией включается поддержка в Spring повторяемых методов, при выбрасывании исключения метод с помощью повторных попыток автоматически повторяется. Такое поведение активируется аннотацией @EnableRetry
.
2) TodoController
@RestController
@RequestMapping("/api/v1/todos")
public class TodoController {
private final TodoService todoService;
@Autowired
public TodoController(TodoService todoService) {
this.todoService = todoService;
}
@PostMapping("")
public String saveTodo(@RequestBody Todo todo) throws SaveTodoException, IllegalArgumentException {
return todoService.saveTodo(todo);
}
}
3) TodoService
В соответствии с полем content выбрасывается SaveTodoException или IllegalArgumentException, затем ожидается запуск методов восстановления:
@Service
@Slf4j
public class TodoService {
@Retryable(
retryFor = { SaveTodoException.class, IllegalArgumentException.class },
maxAttempts = 3,
backoff = @Backoff(delay = 800))
public String saveTodo(Todo todo) throws SaveTodoException, IllegalArgumentException {
if (todo.getContent().equals("SaveTodoException")) {
log.error("Error when todo saving. [SaveTodoException]");
throw new SaveTodoException("Todo did not save");
} else if (todo.getContent().equals("IllegalArgumentException")) {
log.error("Error when todo saving. [IllegalArgumentException]");
throw new IllegalArgumentException("Todo did not save");
}
return "todo saved.";
}
@Recover
public String recoverSaveTodo(SaveTodoException e, Todo todo){
log.info(todo.getContent());
return "recover save todo [SaveTodoException]";
}
@Recover
public String recoverSaveTodo(IllegalArgumentException e, Todo todo){
log.info(todo.getContent());
return "recover save todo [IllegalArgumentException]";
}
}
@Retryable
: этой аннотацией метод обозначается как повторяемый, при выбрасывании определенных исключений он повторяется автоматически. Конфигурация этого поведения образуется такими параметрами аннотации:
retryFor
: этим атрибутом указывается массив типов исключений, для которых повторяется метод. В данном примере метод повторяется при выбрасыванииSaveTodoException
илиIllegalArgumentException
.maxAttempts
: этим атрибутом задается максимум повторов, здесь метод повторяется до трех раз.backoff
: этим атрибутом определяется стратегия задержки повторов. Здесь это фиксированная задержка в 800 миллисекунд между повторами, значение по умолчанию — 1000 мс.
@Recover
: этой аннотацией обозначается метод восстановления для специфического исключения, который выполняется при невыполнении повторяемого метода. Так исключение обрабатывается и происходит восстановление после него.
4) Todo
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Todo {
private String title;
private String content;
}
5) SaveTodoException
public class SaveTodoException extends Exception{
public SaveTodoException(String message) {
super(message);
}
}
Отправим HTTP-запрос
Исключение не выброшено, поэтому первый HTTP-запрос не повторится:
Выбросим исключение:
- SaveTodoException
- IllegalArgumentException
Механизм повторных попыток сработал три раза для каждого исключения, и в методе восстановления клиенту вернулось значение.
Репозиторий на GitHub.
Читайте также:
- Ключевые вопросы для собеседования по Spring Boot в 2023 году. Часть 1
- Лучшие практики написания кода в Spring Boot
- Spring Boot: реализация фабричного метода
Читайте нас в Telegram, VK и Дзен
Перевод статьи Mert ÇAKMAK: Retry Mechanism in Spring Boot(@Retryable and @Recover)