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

Повторная попытка неудачных запросов с помощью ленты Spring Cloud Netflix

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

Автор оригинала: Catalin Burcea.

1. Обзор

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

В этом уроке мы рассмотрим этот механизм повторных попыток.

Во-первых, мы увидим, почему важно, чтобы наши приложения создавались с учетом этой функции. Затем мы создадим и настроим приложение с лентой Spring Cloud Netflix, чтобы продемонстрировать механизм.

2. Мотивация

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

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

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

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

3. Настройка

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

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

3.1. Метеорологическая служба

Давайте построим очень простую службу погоды , которая иногда будет выходить из строя, с кодом состояния HTTP 503 (служба недоступна). Мы смоделируем этот прерывистый сбой, выбрав сбой, когда количество вызовов кратно настраиваемому свойству successful.call.divisor :

@Value("${successful.call.divisor}")
private int divisor;
private int nrOfCalls = 0;

@GetMapping("/weather")
public ResponseEntity weather() {
    LOGGER.info("Providing today's weather information");
    if (isServiceUnavailable()) {
        return new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE);
    }
    LOGGER.info("Today's a sunny day");
    return new ResponseEntity<>("Today's a sunny day", HttpStatus.OK);
}

private boolean isServiceUnavailable() {
    return ++nrOfCalls % divisor != 0;
}

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

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

3.2. Обслуживание Клиентов

Наш второй сервис будет использовать ленту Spring Cloud Netflix.

Во-первых, давайте определим конфигурацию ленточного клиента:

@Configuration
@RibbonClient(name = "weather-service", configuration = RibbonConfiguration.class)
public class WeatherClientRibbonConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}

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

Теперь мы добавим механизм ping для определения доступности службы, а также стратегию циклической балансировки нагрузки, определив класс RibbonConfiguration , включенный в аннотацию @RibbonClient выше:

public class RibbonConfiguration {
 
    @Bean
    public IPing ribbonPing() {
        return new PingUrl();
    }
 
    @Bean
    public IRule ribbonRule() {
        return new RoundRobinRule();
    }
}

Затем нам нужно отключить Eureka от ленточного клиента, так как мы не используем обнаружение служб . Вместо этого мы используем вручную определенный список экземпляров weather-service , доступных для балансировки нагрузки.

Итак, давайте также добавим все это в файл application.yml :

weather-service:
    ribbon:
        eureka:
            enabled: false
        listOfServers: http://localhost:8021, http://localhost:8022

Наконец, давайте построим контроллер и заставим его вызывать серверную службу:

@RestController
public class MyRestController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/client/weather")
    public String weather() {
        String result = this.restTemplate.getForObject("http://weather-service/weather", String.class);
        return "Weather Service Response: " + result;
    }
}

4. Включение механизма повторных попыток

4.1. Настройка приложения.свойства yml

Нам нужно поместить свойства службы погоды в файл application.yml нашего клиентского приложения:

weather-service:
  ribbon:
    MaxAutoRetries: 3
    MaxAutoRetriesNextServer: 1
    retryableStatusCodes: 503, 408
    OkToRetryOnAllOperations: true

В приведенной выше конфигурации используются стандартные свойства ленты, которые нам необходимо определить, чтобы включить повторные попытки:

  • MaxAutoRetries количество повторений неудачного запроса на одном и том же сервере (по умолчанию 0)
  • MaxAutoRetriesNextServer количество серверов, которые нужно попробовать, исключая первый (по умолчанию 0)
  • retryableStatusCodes список кодов состояния HTTP для повторной попытки
  • OkToRetryOnAllOperations когда этому свойству присвоено значение true, повторяются все типы HTTP-запросов, а не только GET (по умолчанию)

Мы собираемся повторить неудачный запрос, когда клиентская служба получит код ответа 503 (служба недоступна) или 408 (тайм-аут запроса).

4.2. Необходимые зависимости

Использование ленты Spring Cloud Netflix Повторная попытка пружины чтобы повторить неудачные запросы.

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


    org.springframework.retry
    spring-retry

4.3. Повторите логику на практике

Наконец, давайте рассмотрим логику повторных попыток на практике.

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

Кроме того, нам необходимо настроить свойство successful.call.divisor в каждом экземпляре, чтобы убедиться, что наши моделируемые службы выходят из строя в разное время:

successful.call.divisor = 5 // instance 1
successful.call.divisor = 2 // instance 2

Далее, давайте также запустим клиентскую службу на порту 8080 и вызовем:

http://localhost:8080/client/weather

Давайте взглянем на консоль weather-service :

weather service instance 1:
    Providing today's weather information
    Providing today's weather information
    Providing today's weather information
    Providing today's weather information

weather service instance 2:
    Providing today's weather information
    Today's a sunny day

Итак, после нескольких попыток (4 на примере 1 и 2 на примере 2) мы получили правильный ответ.

5. Настройка политики Обратного отсчета

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

По умолчанию задержка между повторными попытками отсутствует. Под лентой Spring Cloud используется Spring Retry ‘s NoBackOffPolicy объект, который ничего не делает.

Тем не менее, мы можем переопределить поведение по умолчанию , расширив Фабрику повторных попыток с балансировкой нагрузки ленты класс:

@Component
private class CustomRibbonLoadBalancedRetryFactory 
  extends RibbonLoadBalancedRetryFactory {

    public CustomRibbonLoadBalancedRetryFactory(
      SpringClientFactory clientFactory) {
        super(clientFactory);
    }

    @Override
    public BackOffPolicy createBackOffPolicy(String service) {
        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000);
        return fixedBackOffPolicy;
    }
}

Класс FixedBackOffPolicy обеспечивает фиксированную задержку между повторными попытками. Если мы не установим период отката, по умолчанию будет 1 секунда.

В качестве альтернативы мы можем настроить ExponentialBackOffPolicy или Экспоненциальную политику случайного возврата :

@Override
public BackOffPolicy createBackOffPolicy(String service) {
    ExponentialBackOffPolicy exponentialBackOffPolicy = 
      new ExponentialBackOffPolicy();
    exponentialBackOffPolicy.setInitialInterval(1000);
    exponentialBackOffPolicy.setMultiplier(2); 
    exponentialBackOffPolicy.setMaxInterval(10000);
    return exponentialBackOffPolicy;
}

Здесь начальная задержка между попытками составляет 1 секунду. Затем задержка удваивается для каждой последующей попытки, не превышая 10 секунд: 1000 мс, 2000 мс, 4000 мс, 8000 мс, 10000 мс, 10000 мс…

Кроме того, Политика Экспоненциального случайного отката добавляет случайное значение к каждому периоду сна, не превышая следующего значения. Таким образом, он может дать 1500 мс, 3400 мс, 6200 мс, 9800 мс, 10000 мс, 10000 мс…

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

6. Заключение

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

Затем мы продемонстрировали, как логика повторных попыток работает с помощью приложения REST, поддерживаемого двумя службами загрузки Spring. Лента Spring Cloud Netflix делает это возможным, используя библиотеку повторных попыток Spring.

Наконец, мы увидели, как настроить различные типы задержек между повторными попытками.

Как всегда, исходный код этого учебника доступен на GitHub .