Рубрики
Без рубрики

Как использовать автоматический выключатель в приложении с пружинной загрузкой

В этом посте я покажу, как мы можем использовать шаблон автоматического выключателя в приложении Spring Boot. Вт/ч… Помечен автоматическим выключателем, пружинным загрузчиком, java.

В этом посте я покажу, как мы можем использовать шаблон автоматического выключателя в приложении Spring Boot. Когда я говорю шаблон автоматического выключателя, это архитектурный шаблон. Netflix опубликовал библиотеку Hystrix для работы с автоматическими выключателями. В рамках этого поста я покажу, как мы можем использовать шаблон автоматического выключателя с помощью библиотеки resilence4j в приложении Spring Boot.

В других новостях я недавно выпустил свою книгу Упрощение безопасности Spring/|. Если вам интересно узнать о безопасности Spring, вы можете купить ее здесь .

Что такое автоматический выключатель?

Концепция автоматического выключателя исходит из электротехники. В большинстве электрических сетей автоматические выключатели – это выключатели, которые защищают сеть от повреждений, вызванных перегрузкой по току или коротким замыканием.

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

Автоматический выключатель принимает решение об остановке вызова на основе предыдущей истории вызовов. Но есть альтернативные способы, как он может обрабатывать звонки. Обычно он будет отслеживать предыдущие звонки. Предположим, что 4 из 5 вызовов завершились неудачно или истекло время ожидания, тогда следующий вызов завершится неудачно. Это помогает более активно обрабатывать ошибки с помощью службы вызова, а служба вызывающего абонента может обрабатывать ответ по-другому, позволяя пользователям воспринимать приложение иначе, чем страницу с ошибкой.

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

Библиотека устойчивости

У нас есть наш код, по которому мы вызываем удаленную службу. Модуль автоматического выключателя из библиотеки устойчивость 4 j будет иметь лямбда-выражение для вызова удаленной службы ИЛИ поставщика для извлечения значений из вызова удаленной службы. Я покажу это как часть примера. Автоматический выключатель оформляет этот вызов удаленной службы таким образом, чтобы он мог отслеживать ответы и состояния переключения.

Различные конфигурации библиотеки устойчивости

Чтобы понять концепцию автоматического выключателя, мы рассмотрим различные конфигурации, предлагаемые этой библиотекой.

тип раздвижного окна() – Эта конфигурация в основном помогает в принятии решения о том, как будет работать автоматический выключатель. Существует два типа COUNT_BASED и TIME_BASED. Скользящее окно автоматического выключателя на основе подсчета будет учитывать количество вызовов удаленной службы, в то время как скользящее окно автоматического выключателя на основе ВРЕМЕНИ будет учитывать вызовы удаленной службы за определенный промежуток времени.

порог частоты отказов() – Это настраивает пороговое значение частоты отказов в процентах. Если x процент неудачных вызовов, то автоматический выключатель разомкнется.

slidingWindowSize() – Этот параметр помогает определить количество вызовов, которые следует учитывать при закрытии автоматического выключателя.

порог скорости медленного вызова() – Это настраивает порог скорости медленного вызова в процентах. Если x процент вызовов медленный, то автоматический выключатель разомкнется.

slowCallDurationThreshold – Порог продолжительности времени, при котором вызовы считаются медленными.

минимальное Количество Звонков() – Минимальное количество требуемых вызовов, перед которыми автоматический выключатель может рассчитать частоту ошибок.

игнорировать исключение() – Этот параметр позволяет настроить исключение, которое автоматический выключатель может игнорировать и не будет учитываться при успешном или неудачном вызове удаленной службы.

waitDurationInOpenState() – Продолжительность, в течение которой автоматический выключатель должен оставаться в открытом состоянии, прежде чем перейти в полуоткрытое состояние. Значение по умолчанию – 60 секунд.

Автоматический выключатель на основе подсчета

При использовании библиотеки resilience 4 j всегда можно использовать конфигурации по умолчанию, предлагаемые автоматическим выключателем. Конфигурации по умолчанию основаны на типе скользящего окна НА ОСНОВЕ ПОДСЧЕТА.

Итак, как нам создать автоматический выключатель для типа скользящего окна на ОСНОВЕ подсчета?

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 вызовов для переключения автоматического выключателя в закрытое состояние. Если 65 процентов вызовов являются медленными, при этом медленные вызовы длятся более 3 секунд, автоматический выключатель разомкнется.

Реестр автоматических выключателей – это завод по созданию автоматического выключателя.

Автоматический выключатель на основе времени

Теперь о временном выключателе.

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 с помощью RestTemplate. Мы украсим наш звонок для ОТДЫХА через автоматический выключатель.

Приложение книги хранит информацию о книгах в таблице базы данных MySQL библиотечные книги . Контроллер REST для этого приложения имеет ПОЛУЧИТЬ и ПОСТ методы.

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 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);
        }
    }
}

С другой стороны, наше приложение Демонстрация автоматического выключателя имеет контроллер с шаблоном thymeleaf, чтобы пользователь мог получить доступ к приложению в браузере.

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

    @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 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.

Я автоматически подключил боб для счетного выключателя . Для демонстрационных целей – я буду звонить в службу REST 15 раз подряд, чтобы получить все книги. Таким образом, я могу имитировать прерывание на стороне службы ОТДЫХА.

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

В этой демонстрации мы вызываем нашу службу REST последовательно, но вызовы удаленных служб также могут выполняться параллельно. Автоматический выключатель по-прежнему будет отслеживать результаты независимо от последовательных или параллельных вызовов.

Демонстрация

Давайте посмотрим, как будет работать автоматический выключатель в демонстрационном режиме. Моя служба REST работает на порту 8443, а мое демонстрационное приложение автоматического выключателя работает на порту 8743.

Сначала я запускаю оба приложения и захожу на домашнюю страницу Демонстрационного приложения автоматического выключателя . Домашняя страница содержит ссылку для просмотра всех книг из магазина.

Теперь, чтобы имитировать некоторые ошибки, я добавил следующий код в свой вызов RestTemplate, который в основном спит в течение 3 секунд, прежде чем вернуть результат вызова REST.

    public List getAllBooksFromLibrary ()
    {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);

        ResponseEntity responseEntity;
        long startTime = System.currentTimeMillis();
        LOGGER.info("Start time = {}", startTime);
        try
        {
            responseEntity= restTemplate.exchange(buildUrl(),
                    HttpMethod.GET, null, new ParameterizedTypeReference()
                    {});
            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 секунд. Как только я нажму на ссылку здесь, я получу результат, но мой автоматический выключатель будет разомкнут и не разрешит будущие вызовы, пока он не будет находиться в полуоткрытом состоянии или закрытом состоянии.

Вы заметите, что мы начали получать исключение NotPermittedException, когда автоматический выключатель находился в ОТКРЫТОМ состоянии. Кроме того, автоматический выключатель был открыт при выполнении 10 вызовов. Это связано с тем, что размер нашего раздвижного окна составляет 10.

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

Теперь давайте переключим COUNT_BASED автоматический выключатель на ОСНОВАННЫЙ НА ВРЕМЕНИ автоматический выключатель. В ОСНОВАННОМ на ВРЕМЕНИ выключателе мы отключим нашу службу ОТДЫХА через секунду, а затем перейдем по ссылке здесь с домашней страницы. Если 70 процентов звонков за последние 10 секунд завершатся неудачей, наш автоматический выключатель разомкнется.

Поскольку служба REST закрыта, мы увидим следующие ошибки в Демоверсии обрыва цепи приложение

Мы увидим количество ошибок до того, как автоматический выключатель окажется в РАЗОМКНУТОМ состоянии.

В одной конфигурации мы всегда можем добавить, как долго мы хотим держать автоматический выключатель в открытом состоянии. Для демонстрации я добавил, что автоматический выключатель будет находиться в открытом состоянии в течение 10 секунд.

Как обращаться с РАЗОМКНУТЫМИ автоматическими выключателями?

Возникает один вопрос: как вы справляетесь с РАЗОМКНУТЫМИ автоматическими выключателями? К счастью, устойчивость предлагает резервную конфигурацию с Декораторами утилитой. В большинстве случаев вы всегда можете настроить это так, чтобы получить результат предыдущих успешных результатов, чтобы пользователи все еще могли работать с приложением.

Вывод

В этом посте я рассказал о том, как использовать автоматический выключатель в приложении Spring Boot. Код для этой демонстрации доступен здесь .

В этой демонстрации я не описал, как отслеживать эти события автоматического выключателя, поскольку устойчивость 4 j библиотека позволяет хранить эти события с метриками, которые можно отслеживать с помощью системы мониторинга.

Если вам понравился этот пост, подумайте о подписке на мой блог здесь .

Оригинал: “https://dev.to/betterjavacode/how-to-use-circuit-breaker-in-a-spring-boot-application-1mp”