Механизм повторных попыток в Spring Boot: @Retryable и @Recover

Рассмотрим механизм повторных попыток в Spring Boot с аннотациями @Retryable и @Recovery.

Источник

Что такое «механизм повторных попыток»?

Это типовой механизм обработки временных сбоев в распределенных системах. А временный сбой  —  это временная ошибка, которая устраняется повторением операции. Механизмом повторных попыток неудачная операция повторяется, пока она не выполнится или не достигнуто максимальное число попыток.

Этот механизм применяется в следующих сценариях.

  1. Сбоя сети или связи: взаимодействие приложения с внешними службами или базами данных по сети чревато сетевыми проблемами или недоступностью служб, при сетевых сбоях операция этим механизмом повторяется.
  2. Операции с базами данных: при работе с БД случаются взаимоблокировки.
  3. Обработки пользовательских исключений: специфических исключений, повторяемых при определенных условиях.

Реализация в 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-запрос

[POST] http://localhost:8080/api/v1/todos

Исключение не выброшено, поэтому первый HTTP-запрос не повторится:

Выбросим исключение:

  • SaveTodoException
  • IllegalArgumentException

Механизм повторных попыток сработал три раза для каждого исключения, и в методе восстановления клиенту вернулось значение.

Репозиторий на GitHub.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Mert ÇAKMAK: Retry Mechanism in Spring Boot(@Retryable and @Recover)

Предыдущая статьяПорты Docker: что вы на самом деле открываете?
Следующая статьяСоздание базовой чат-системы с использованием node.js и socket.io