Автор оригинала: 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 ResponseEntityweather() { 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 .