[Java] Как использовать шаблон Circuit Breaker в приложении Spring Boot (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В этом посте я покажу, как мы можем использовать шаблон Circuit Breaker в приложении Spring Boot. Когда я говорю «шаблон Circuit Breaker» имеется в виду архитектурный шаблон автоматического выключателя. Netflix опубликовал библиотеку Hysterix для работы с автоматическими выключателями. В рамках этого поста я покажу, как мы можем использовать шаблон автоматического выключателя, используя библиотеку resilence4j в приложении Spring Boot.
Изображение с сайта Pixabay - Автор Jürgen DiermaierЧто такое автоматический выключатель?Концепция автоматического выключателя пришла из электротехники. В большинстве электрических сетей автоматические выключатели представляют собой выключатели, которые защищают сеть от повреждений, вызванных перегрузкой по току или коротким замыканием.Точно так же в программном обеспечении автоматический выключатель останавливает вызов удаленный сервис, если мы знаем, что вызов этого удаленного сервиса либо завершится сбоем, либо истечет время ожидания. Преимущество этого подхода заключается в экономии ресурсов и упреждающем поиске и устранении неисправностей удаленных вызовов процедур.Автоматический выключатель принимает решение об остановке вызова на основе предыдущей истории вызовов. Но есть альтернативные способы обработки вызовов. Обычно автоматический выключатель отслеживает предыдущие звонки. Предположим, что 4 из 5 вызовов завершились неудачно или истекло время, тогда следующий вызов завершится ошибкой. Это помогает более активно обрабатывать ошибки при вызове сервиса, а сервис вызывающего абонента может обрабатывать ответ по-другому, предоставляя пользователям приложения другой вариант, чем просто страницу с ошибкой.Другой случай срабатывания автоматического выключателя — это, когда вызовы удаленного сервиса не работают в течение определенного времени. Автоматический выключатель сработает и не допустит следующего вызова, пока удаленное обслуживание не исправит ошибку.Библиотека Resilience4JУ нас есть код, в котором мы вызываем удаленный сервис. Модуль автоматического выключателя из resilience4j библиотеки будет иметь лямбда-выражение для вызова удаленный сервис supplier для получения значений из вызова удаленного сервиса. Я покажу это на примере. Автоматический выключатель украшает этот вызов удаленного обслуживания таким образом, чтобы он мог отслеживать ответы и состояния переключателя.Различные конфигурации библиотеки Resilience4jЧтобы понять концепцию автоматического выключателя, мы рассмотрим различные конфигурации, предлагаемые этой библиотекой.
- slidingWindowType() — эта конфигурация называется скользящее окно. В основном именно она помогает принять решение о том, как будет работать автоматический выключатель. Есть два типа скользящих окон: COUNT_BASED и TIME_BASED. Скользящее окно автоматического выключателя COUNT_BASED будет учитывать количество вызовов удаленного сервиса, в то время как TIME_BASED скользящее окно автоматического выключателя будет учитывать вызовы удаленного сервиса в течение определенного периода времени.
- failureRateThreshold() — настраивает порог частоты отказов в процентах. Если x процентов вызовов не работают, выключатель отключается.
- slidingWindowSize() — эта настройка помогает определить количество вызовов, которые следует учитывать при включении автоматического выключателя.
- slowCallRateThreshold() — настраивает порог низкой скорости вызова в процентах. Если x процентов вызовов являются медленными, автоматический выключатель отключается.
- slowCallDurationThreshold — настраивает порог продолжительности времени, при котором вызовы считаются медленными.
- minimumNumberOfCalls() — минимальное необходимое количество вызовов, перед которым автоматический выключатель может рассчитать частоту ошибок.
- ignoreException() — этот параметр позволяет вам настроить исключение, которое автоматический выключатель может игнорировать и не будет учитываться при успешном или неудачном вызове удаленного сервиса.
- waitDurationInOpenState() — Продолжительность, в течение которой автоматический выключатель должен оставаться в разомкнутом состоянии перед переходом в полуоткрытое состояние. Значение по умолчанию - 60 секунд.
COUNT-BASED автоматический выключатель При использовании resilience4j библиотеки всегда можно использовать конфигурации по умолчанию, которые предлагает автоматический выключатель. Конфигурации по умолчанию основаны на типе COUNT-BASED скользящего окна.Так как же нам создать автоматический выключатель для скользящего окна типа COUNT-BASED?
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.slidingWindowSize(10)
.slowCallRateThreshold(65.0f)
.slowCallDurationThreshold(Duration.ofSeconds(3))
.build();
CircuitBreakerRegistry circuitBreakerRegistry =
CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BooksSearchServiceBasedOnCount");
В приведенном выше примере мы создаем конфигурацию автоматического выключателя, которая включает тип скользящего окна COUNT_BASED. Этот автоматический выключатель записывает результат 10 вызовов для переключения автоматического выключателя в closed состояние. Если 65% вызовов являются медленными, а продолжительность медленных вызовов превышает 3 секунды, автоматический выключатель отключается.CircuitBreakerRegistry — фабрика по созданию выключателя.Time-Based автоматический выключатель Теперь об Time-Based автоматическом выключателе.
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.minimumNumberOfCalls(3)
.slidingWindowSize(10)
.failureRateThreshold(70.0f)
.build();
CircuitBreakerRegistry circuitBreakerRegistry =
CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BookSearchServiceBasedOnTime");
В приведенном выше примере мы создаем конфигурацию автоматического выключателя, которая включает в себя скользящее окно типа TIME_BASED. Автоматический выключатель фиксирует отказ вызовов после минимум 3 вызовов. Если 70 процентов вызовов терпят неудачу, срабатывает автоматический выключатель.Пример автоматического выключателя в приложении Spring BootМы рассмотрели необходимые понятия об автоматическом выключателе. Теперь я покажу, что мы можем использовать автоматический выключатель в приложении Spring Boot.С одной стороны, у нас есть REST приложение, BooksApplication которое хранит основные сведения о библиотечных книгах. С другой стороны, у нас есть приложение, Circuitbreakerdemo которое вызывает приложение REST с помощью RestTemplate. Декорируем наш REST-вызов с помощью автоматического выключателя.BooksApplication хранит информацию о книгах в таблице базы данных MySQL librarybooks. В REST контроллере этого приложения есть GET и POST методы.
package com.betterjavacode.books.controllers;
import com.betterjavacode.books.daos.BookDao;
import com.betterjavacode.books.models.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@CrossOrigin("https://localhost:8443")
@RestController
@RequestMapping("/v1/library")
public class BookController
{
@Autowired
BookDao bookDao;
@GetMapping("/books")
public ResponseEntity<List> getAllBooks(@RequestParam(required = false) String bookTitle)
{
try
{
List listOfBooks = new ArrayList<>();
if(bookTitle == null || bookTitle.isEmpty())
{
bookDao.findAll().forEach(listOfBooks::add);
}
else
{
bookDao.findByTitleContaining(bookTitle).forEach(listOfBooks::add);
}
if(listOfBooks.isEmpty())
{
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(listOfBooks, HttpStatus.OK);
}
catch (Exception e)
{
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/books/{id}")
public ResponseEntity getBookById(@PathVariable("id") long id)
{
try
{
Optional bookOptional = bookDao.findById(id);
return new ResponseEntity<>(bookOptional.get(), HttpStatus.OK);
}
catch (Exception e)
{
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/books")
public ResponseEntity addABookToLibrary(@RequestBody Book book)
{
try
{
Book createdBook = bookDao.save(new Book(book.getTitle(), book.getAuthor(),
book.getIsbn()));
return new ResponseEntity<>(createdBook, HttpStatus.CREATED);
}
catch (Exception e)
{
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/books/{id}")
public ResponseEntity updateABook(@PathVariable("id") long id, @RequestBody Book book)
{
Optional bookOptional = bookDao.findById(id);
if(bookOptional.isPresent())
{
Book updatedBook = bookOptional.get();
updatedBook.setTitle(book.getTitle());
updatedBook.setAuthor(book.getAuthor());
updatedBook.setIsbn(book.getIsbn());
return new ResponseEntity<>(bookDao.save(updatedBook), HttpStatus.OK);
}
else
{
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/books/{id}")
public ResponseEntity deleteABook(@PathVariable("id") long id)
{
try
{
bookDao.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
catch (Exception e)
{
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
С другой стороны, в нашем приложении Circuitbreakerdemo есть контроллер с шаблоном thymeleaf, поэтому пользователь может получить доступ к приложению в браузере.В демонстрационных целях я определил CircuitBreaker в отдельном компоненте, который я буду использовать в своем service классе.
@Bean
public CircuitBreaker countCircuitBreaker()
{
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.slidingWindowSize(10)
.slowCallRateThreshold(65.0f)
.slowCallDurationThreshold(Duration.ofSeconds(3))
.build();
CircuitBreakerRegistry circuitBreakerRegistry =
CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BooksSearchServiceBasedOnCount");
return cb;
}
@Bean
public CircuitBreaker timeCircuitBreaker()
{
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.minimumNumberOfCalls(3)
.slidingWindowSize(10)
.failureRateThreshold(70.0f)
.build();
CircuitBreakerRegistry circuitBreakerRegistry =
CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BookSearchServiceBasedOnTime");
return cb;
}
Я определил два компонента: один для автоматического выключателя на основе счетчика, а другой - для автоматического выключателя на основе времени.BookStoreService будет содержать вызывающее приложение BooksApplication и отображать доступные книги. Этот сервис будет выглядеть так:
@Controller
public class BookStoreService
{
private static final Logger LOGGER = LoggerFactory.getLogger(BookStoreService.class);
@Autowired
public BookManager bookManager;
@Autowired
private CircuitBreaker countCircuitBreaker;
@RequestMapping(value = "/home", method= RequestMethod.GET)
public String home(HttpServletRequest request, Model model)
{
return "home";
}
@RequestMapping(value = "/books", method=RequestMethod.GET)
public String books(HttpServletRequest request, Model model)
{
Supplier<List> booksSupplier =
countCircuitBreaker.decorateSupplier(() -> bookManager.getAllBooksFromLibrary());
LOGGER.info("Going to start calling the REST service with Circuit Breaker");
List books = null;
for(int i = 0; i < 15; i++)
{
try
{
LOGGER.info("Retrieving books from returned supplier");
books = booksSupplier.get();
}
catch(Exception e)
{
LOGGER.error("Could not retrieve books from supplier", e);
}
}
model.addAttribute("books", books);
return "books";
}
}
Поэтому, когда пользователь кликает на ссылку на главной странице, мы получаем книги из нашей REST-сервиса BooksApplication.Я автоматически подключил бин для countCircuitBreaker. В демонстрационных целях я буду вызывать REST сервис 15 раз подряд, чтобы получить все книги. Таким образом, я смогу имитировать прерывание на стороне моей REST-сервиса.Наш автоматический выключатель декорирует сервис supplier, который выполняет REST-вызов удаленного сервиса и сохраняет результат нашего удаленного вызова.В этой демонстрации мы вызываем наш REST-сервис последовательно, но вызовы удаленного сервиса также могут происходить параллельно. Автоматический выключатель по-прежнему будет отслеживать результаты независимо от последовательных или параллельных вызовов.ДемоДавайте теперь посмотрим на живой демонстрации, как автоматический выключатель будет работать. Мой REST-сервис работает на порту 8443, а мое Circuitbreakerdemo приложение - на порту 8743.Сначала я запускаю оба приложения и открываю домашнюю страницу Circuitbreakerdemo приложения. На главной странице есть ссылка для просмотра всех книг из магазина.
Теперь, чтобы смоделировать некоторые ошибки, я добавил следующий код в свой вызов RestTemplate, который в основном спит в течение 3 секунд, прежде чем вернуть результат вызова REST.
public List getAllBooksFromLibrary ()
{
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
ResponseEntity<List> responseEntity;
long startTime = System.currentTimeMillis();
LOGGER.info("Start time = {}", startTime);
try
{
responseEntity= restTemplate.exchange(buildUrl(),
HttpMethod.GET, null, new ParameterizedTypeReference<List>()
{});
if(responseEntity != null && responseEntity.hasBody())
{
Thread.sleep(3000);
LOGGER.info("Total time to retrieve results = {}",
System.currentTimeMillis() - startTime);
return responseEntity.getBody();
}
}
catch (URISyntaxException | InterruptedException e)
{
LOGGER.error("URI has a wrong syntax", e);
}
LOGGER.info("No result found, returning an empty list");
return new ArrayList<>();
}
Короче говоря, мой контур автоматического выключателя будет вызывать удаленный сервис достаточное количество раз, чтобы преодолеть порог в 65 процентов медленных вызовов продолжительностью более 3 секунд. Как только я нажму на ссылку here, я получу результат, но мой автоматический выключатель будет разомкнут и не будет разрешать дальнейшие вызовы, пока он будет в состоянии half-open либо closed.
Вы сожете заметить, что мы начали получать исключение, CallNotPermittedException, когда автоматический выключатель был в состоянии OPEN. Кроме того, выключатель был отключен при выполнении 10 вызовов. Это потому, что размер нашего скользящего окна равен 10.Другой способ - смоделировать ошибку, отключив REST сервис или сервис базы данных. Таким образом, вызовы REST могут занять больше времени, чем требуется.Теперь давайте переключим COUNT_BASED автоматический выключатель на TIME_BASEDавтоматический выключатель. В TIME_BASED автоматическом выключателе мы отключим наш REST-сервис через секунду, а затем щелкнем hereссылку с домашней страницы. Если 70 процентов вызовов за последние 10 секунд не работают, наш автоматический выключатель сработает.Поскольку REST-сервис закрыт, мы увидим следующие ошибки в Circuitbreakdemo приложении
Мы увидим несколько ошибок до того, как автоматический выключатель будет в OPENсостоянии.
Еще одна конфигурация, которую мы всегда можем добавить, как долго мы хотим держать выключатель в разомкнутом состоянии. Для демонстрации я добавил, что автоматический выключатель будет в разомкнутом состоянии в течение 10 секунд.Как обращаться с OPEN выключателями?Возникает вопрос, как обращаться с OPEN выключателями? К счастью, resilience4jпредлагает резервную конфигурацию с утилитой Decorators. В большинстве случаев вы всегда можете настроить ее так, чтобы получить результат предыдущих успешных результатов, чтобы пользователи могли работать с приложением.ВыводВ этом посте я рассказал, как использовать автоматический выключатель в приложении Spring Boot. Код для этой демонстрации доступен здесь .В этой демонстрации я не рассмотрел, как отслеживать события выключателя, поскольку библиотека resilience4j позволяет сохранять эти события с метриками, которые можно отслеживать с помощью системы мониторинга.Рекомендации для прочтения
- Библиотека Resilience4J
- Автоматический выключатель с Resilience4j - Circuit Breaker
===========
Источник:
habr.com
===========
===========
Автор оригинала: Yogesh Mali
===========Похожие новости:
- [JavaScript, ReactJS, Карьера в IT-индустрии, TypeScript] Яндекс.Практикум запустил курс «React-разработчик»
- [Java, Анализ и проектирование систем, Промышленное программирование] Как катать релизы несколько раз в день и спать спокойно. Доклад Яндекса
- [Java] Использование Google Protocol Buffers (protobuf) в Java (перевод)
- [Разработка веб-сайтов, JavaScript, Программирование, ReactJS] Разрабатываем чат на React с использованием Socket.IO
- [JavaScript, API] ExtendScript Работа с композициями
- [Open source, Виртуализация, Карьера в IT-индустрии, Openshift] Поваренная книга Quarkus Cookbook, бесплатный Developer Sandbox for OpenShift и руководство CentOS Project
- [Java] Запись событий Spring при тестировании приложений Spring Boot (перевод)
- [Высокая производительность, JavaScript, Программирование, Клиентская оптимизация, TypeScript] JavaScript нанобенчмарки и преждевременные тормоза
- [Java] Методы расширения в Java
- [Java, Параллельное программирование] Реактивное программирование на Java: как, зачем и стоит ли? Часть I
Теги для поиска: #_java, #_spring_boot, #_patterns, #_circuit_breaker, #_resilience, #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 10:36
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В этом посте я покажу, как мы можем использовать шаблон Circuit Breaker в приложении Spring Boot. Когда я говорю «шаблон Circuit Breaker» имеется в виду архитектурный шаблон автоматического выключателя. Netflix опубликовал библиотеку Hysterix для работы с автоматическими выключателями. В рамках этого поста я покажу, как мы можем использовать шаблон автоматического выключателя, используя библиотеку resilence4j в приложении Spring Boot. Изображение с сайта Pixabay - Автор Jürgen DiermaierЧто такое автоматический выключатель?Концепция автоматического выключателя пришла из электротехники. В большинстве электрических сетей автоматические выключатели представляют собой выключатели, которые защищают сеть от повреждений, вызванных перегрузкой по току или коротким замыканием.Точно так же в программном обеспечении автоматический выключатель останавливает вызов удаленный сервис, если мы знаем, что вызов этого удаленного сервиса либо завершится сбоем, либо истечет время ожидания. Преимущество этого подхода заключается в экономии ресурсов и упреждающем поиске и устранении неисправностей удаленных вызовов процедур.Автоматический выключатель принимает решение об остановке вызова на основе предыдущей истории вызовов. Но есть альтернативные способы обработки вызовов. Обычно автоматический выключатель отслеживает предыдущие звонки. Предположим, что 4 из 5 вызовов завершились неудачно или истекло время, тогда следующий вызов завершится ошибкой. Это помогает более активно обрабатывать ошибки при вызове сервиса, а сервис вызывающего абонента может обрабатывать ответ по-другому, предоставляя пользователям приложения другой вариант, чем просто страницу с ошибкой.Другой случай срабатывания автоматического выключателя — это, когда вызовы удаленного сервиса не работают в течение определенного времени. Автоматический выключатель сработает и не допустит следующего вызова, пока удаленное обслуживание не исправит ошибку.Библиотека Resilience4JУ нас есть код, в котором мы вызываем удаленный сервис. Модуль автоматического выключателя из resilience4j библиотеки будет иметь лямбда-выражение для вызова удаленный сервис supplier для получения значений из вызова удаленного сервиса. Я покажу это на примере. Автоматический выключатель украшает этот вызов удаленного обслуживания таким образом, чтобы он мог отслеживать ответы и состояния переключателя.Различные конфигурации библиотеки Resilience4jЧтобы понять концепцию автоматического выключателя, мы рассмотрим различные конфигурации, предлагаемые этой библиотекой.
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) .slidingWindowSize(10) .slowCallRateThreshold(65.0f) .slowCallDurationThreshold(Duration.ofSeconds(3)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BooksSearchServiceBasedOnCount"); CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED) .minimumNumberOfCalls(3) .slidingWindowSize(10) .failureRateThreshold(70.0f) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BookSearchServiceBasedOnTime"); package com.betterjavacode.books.controllers;
import com.betterjavacode.books.daos.BookDao; import com.betterjavacode.books.models.Book; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; import java.util.Optional; @CrossOrigin("https://localhost:8443") @RestController @RequestMapping("/v1/library") public class BookController { @Autowired BookDao bookDao; @GetMapping("/books") public ResponseEntity<List> getAllBooks(@RequestParam(required = false) String bookTitle) { try { List listOfBooks = new ArrayList<>(); if(bookTitle == null || bookTitle.isEmpty()) { bookDao.findAll().forEach(listOfBooks::add); } else { bookDao.findByTitleContaining(bookTitle).forEach(listOfBooks::add); } if(listOfBooks.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(listOfBooks, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } } @GetMapping("/books/{id}") public ResponseEntity getBookById(@PathVariable("id") long id) { try { Optional bookOptional = bookDao.findById(id); return new ResponseEntity<>(bookOptional.get(), HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } } @PostMapping("/books") public ResponseEntity addABookToLibrary(@RequestBody Book book) { try { Book createdBook = bookDao.save(new Book(book.getTitle(), book.getAuthor(), book.getIsbn())); return new ResponseEntity<>(createdBook, HttpStatus.CREATED); } catch (Exception e) { return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } } @PutMapping("/books/{id}") public ResponseEntity updateABook(@PathVariable("id") long id, @RequestBody Book book) { Optional bookOptional = bookDao.findById(id); if(bookOptional.isPresent()) { Book updatedBook = bookOptional.get(); updatedBook.setTitle(book.getTitle()); updatedBook.setAuthor(book.getAuthor()); updatedBook.setIsbn(book.getIsbn()); return new ResponseEntity<>(bookDao.save(updatedBook), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } @DeleteMapping("/books/{id}") public ResponseEntity deleteABook(@PathVariable("id") long id) { try { bookDao.deleteById(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } } @Bean
public CircuitBreaker countCircuitBreaker() { CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) .slidingWindowSize(10) .slowCallRateThreshold(65.0f) .slowCallDurationThreshold(Duration.ofSeconds(3)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BooksSearchServiceBasedOnCount"); return cb; } @Bean public CircuitBreaker timeCircuitBreaker() { CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED) .minimumNumberOfCalls(3) .slidingWindowSize(10) .failureRateThreshold(70.0f) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("BookSearchServiceBasedOnTime"); return cb; } @Controller
public class BookStoreService { private static final Logger LOGGER = LoggerFactory.getLogger(BookStoreService.class); @Autowired public BookManager bookManager; @Autowired private CircuitBreaker countCircuitBreaker; @RequestMapping(value = "/home", method= RequestMethod.GET) public String home(HttpServletRequest request, Model model) { return "home"; } @RequestMapping(value = "/books", method=RequestMethod.GET) public String books(HttpServletRequest request, Model model) { Supplier<List> booksSupplier = countCircuitBreaker.decorateSupplier(() -> bookManager.getAllBooksFromLibrary()); LOGGER.info("Going to start calling the REST service with Circuit Breaker"); List books = null; for(int i = 0; i < 15; i++) { try { LOGGER.info("Retrieving books from returned supplier"); books = booksSupplier.get(); } catch(Exception e) { LOGGER.error("Could not retrieve books from supplier", e); } } model.addAttribute("books", books); return "books"; } } Теперь, чтобы смоделировать некоторые ошибки, я добавил следующий код в свой вызов RestTemplate, который в основном спит в течение 3 секунд, прежде чем вернуть результат вызова REST. public List getAllBooksFromLibrary ()
{ HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); ResponseEntity<List> responseEntity; long startTime = System.currentTimeMillis(); LOGGER.info("Start time = {}", startTime); try { responseEntity= restTemplate.exchange(buildUrl(), HttpMethod.GET, null, new ParameterizedTypeReference<List>() {}); if(responseEntity != null && responseEntity.hasBody()) { Thread.sleep(3000); LOGGER.info("Total time to retrieve results = {}", System.currentTimeMillis() - startTime); return responseEntity.getBody(); } } catch (URISyntaxException | InterruptedException e) { LOGGER.error("URI has a wrong syntax", e); } LOGGER.info("No result found, returning an empty list"); return new ArrayList<>(); } Вы сожете заметить, что мы начали получать исключение, CallNotPermittedException, когда автоматический выключатель был в состоянии OPEN. Кроме того, выключатель был отключен при выполнении 10 вызовов. Это потому, что размер нашего скользящего окна равен 10.Другой способ - смоделировать ошибку, отключив REST сервис или сервис базы данных. Таким образом, вызовы REST могут занять больше времени, чем требуется.Теперь давайте переключим COUNT_BASED автоматический выключатель на TIME_BASEDавтоматический выключатель. В TIME_BASED автоматическом выключателе мы отключим наш REST-сервис через секунду, а затем щелкнем hereссылку с домашней страницы. Если 70 процентов вызовов за последние 10 секунд не работают, наш автоматический выключатель сработает.Поскольку REST-сервис закрыт, мы увидим следующие ошибки в Circuitbreakdemo приложении Мы увидим несколько ошибок до того, как автоматический выключатель будет в OPENсостоянии. Еще одна конфигурация, которую мы всегда можем добавить, как долго мы хотим держать выключатель в разомкнутом состоянии. Для демонстрации я добавил, что автоматический выключатель будет в разомкнутом состоянии в течение 10 секунд.Как обращаться с OPEN выключателями?Возникает вопрос, как обращаться с OPEN выключателями? К счастью, resilience4jпредлагает резервную конфигурацию с утилитой Decorators. В большинстве случаев вы всегда можете настроить ее так, чтобы получить результат предыдущих успешных результатов, чтобы пользователи могли работать с приложением.ВыводВ этом посте я рассказал, как использовать автоматический выключатель в приложении Spring Boot. Код для этой демонстрации доступен здесь .В этой демонстрации я не рассмотрел, как отслеживать события выключателя, поскольку библиотека resilience4j позволяет сохранять эти события с метриками, которые можно отслеживать с помощью системы мониторинга.Рекомендации для прочтения
=========== Источник: habr.com =========== =========== Автор оригинала: Yogesh Mali ===========Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 10:36
Часовой пояс: UTC + 5