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

Ограничение скорости в весеннем облаке Netflix Зуул

Узнайте, как оценить лимитные запросы с помощью весеннего облака Зулус

Автор оригинала: Ganesh Pagade.

1. Введение

Весеннее облако Netflix Зуул является открытым исходным кодом шлюз, который обертывает Netflix Зуул . Он добавляет некоторые специфические функции для приложений Spring Boot. К сожалению, ограничение скорости не предоставляется из коробки.

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

2. Конфигурация Maven

В дополнение к зависимости от весеннего облака Netflix Зуул, мы должны добавить Весеннее облако Зуул RateLimit к заявлению нашего пом.xml :


    org.springframework.cloud
    spring-cloud-starter-netflix-zuul


    com.marcosbarbero.cloud
    spring-cloud-zuul-ratelimit
    2.2.0.RELEASE

3. Пример контроллера

Firstly, let’s create a couple of REST endpoints on which we will apply the rate limits.

Below is a simple Spring Controller class with two endpoints:

@Controller
@RequestMapping("/greeting")
public class GreetingController {

    @GetMapping("/simple")
    public ResponseEntity getSimple() {
        return ResponseEntity.ok("Hi!");
    }

    @GetMapping("/advanced")
    public ResponseEntity getAdvanced() {
        return ResponseEntity.ok("Hello, how you doing?");
    }
}

As we can see, there is no code specific to rate limit the endpoints. This is because we’ll configure that in our Zuul properties within the application.yml file. Thus, keeping our code decoupled.

4. Куул Свойства

Во-вторых, давайте добавим следующие свойства Зуула в нашем application.yml файл:

zuul:
  routes:
    serviceSimple:
      path: /greeting/simple
      url: forward:/
    serviceAdvanced:
      path: /greeting/advanced
      url: forward:/
  ratelimit:
    enabled: true
    repository: JPA
    policy-list:
      serviceSimple:
        - limit: 5
          refresh-interval: 60
          type:
            - origin
      serviceAdvanced:
        - limit: 1
          refresh-interval: 2
          type:
            - origin
  strip-prefix: true

Under zuul.routes we provide the endpoint details. And under zuul.ratelimit.policy-list, we provide the rate limit configurations for our endpoints. The limit property specifies the number of times the endpoint can be called within the refresh-interval .

Как мы видим, мы добавили ограничение скорости 5 запросов на 60 секунд для сервисSimple конечная точка. В отличие от этого, serviceAdvanced имеет ограничение скорости 1 запрос в течение 2 секунд.

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

  • происхождение – ограничение скорости в зависимости от запроса на происхождение пользователя
  • URL – ограничение скорости, основанное на пути запроса службы ниже по течению
  • пользовательские – ограничение скорости на основе подлинного имени пользователя или «анонимного»
  • Нет значения – действует как глобальная конфигурация на одну службу. Чтобы использовать этот подход, просто не устанавливайте парам ‘тип’

5. Тестирование лимита тарифов

5.1. Запрос в пределах предела ставки

Далее, давайте проверить предел скорости:

@Test
public void whenRequestNotExceedingCapacity_thenReturnOkResponse() {
    ResponseEntity response = restTemplate.getForEntity(SIMPLE_GREETING, String.class);
    assertEquals(OK, response.getStatusCode());

    HttpHeaders headers = response.getHeaders();
    String key = "rate-limit-application_serviceSimple_127.0.0.1";

    assertEquals("5", headers.getFirst(HEADER_LIMIT + key));
    assertEquals("4", headers.getFirst(HEADER_REMAINING + key));
    assertThat(
      parseInt(headers.getFirst(HEADER_RESET + key)),
      is(both(greaterThanOrEqualTo(0)).and(lessThanOrEqualTo(60000)))
    );
}

Здесь мы делаем один звонок к конечной точке /приветствие/простое . Запрос является успешным, так как он находится в пределах предела скорости.

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

X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1: 5
X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1: 4
X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1: 60000

Другими словами:

  • X-RateLimit-Limit-«ключ»: предельный настроен для конечной точки
  • X-RateLimit-Оставаясь-ключ: оставшееся количество попыток вызвать конечную точку
  • X-RateLimit-Перезагрузка-ключ: оставшееся количество миллисекунд обновление интервала настроен для конечной точки

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

X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1: 5
X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1: 3
X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1: 57031

Обратите внимание на уменьшенное оставшееся количество попыток и оставшееся количество миллисекунд.

5.2. Запрос о превышении лимита ставки

Давайте посмотрим, что произойдет, когда мы превышаем предел скорости:

@Test
public void whenRequestExceedingCapacity_thenReturnTooManyRequestsResponse() throws InterruptedException {
    ResponseEntity response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
    assertEquals(OK, response.getStatusCode());
    
    for (int i = 0; i < 2; i++) {
        response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
    }

    assertEquals(TOO_MANY_REQUESTS, response.getStatusCode());

    HttpHeaders headers = response.getHeaders();
    String key = "rate-limit-application_serviceAdvanced_127.0.0.1";

    assertEquals("1", headers.getFirst(HEADER_LIMIT + key));
    assertEquals("0", headers.getFirst(HEADER_REMAINING + key));
    assertNotEquals("2000", headers.getFirst(HEADER_RESET + key));

    TimeUnit.SECONDS.sleep(2);

    response = this.restTemplate.getForEntity(ADVANCED_GREETING, String.class);
    assertEquals(OK, response.getStatusCode());
}

Здесь мы называем конечную точку /приветствие/продвинутые дважды в быстрой последовательности. Так как мы настроили ограничение скорости как один запрос на 2 секунды, второй вызов не . В результате код ошибки 429 ( Слишком много запросов) возвращается клиенту.

Ниже приведены заготовки, возвращенные по достигнутой ставке:

X-RateLimit-Limit-rate-limit-application_serviceAdvanced_127.0.0.1: 1
X-RateLimit-Remaining-rate-limit-application_serviceAdvanced_127.0.0.1: 0
X-RateLimit-Reset-rate-limit-application_serviceAdvanced_127.0.0.1: 268

После этого мы спим 2 секунды. Это обновление интервала настроен для конечной точки. Наконец, мы снова заготовим конечную точку и получим успешный ответ.

6. Пользовательский генератор ключей

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

Например, это можно сделать, создав пользовательский RateLimitKeyГенератор реализация. Мы можем добавить дополнительные квалификаторы или что-то совершенно другое:

@Bean
public RateLimitKeyGenerator rateLimitKeyGenerator(RateLimitProperties properties, 
  RateLimitUtils rateLimitUtils) {
    return new DefaultRateLimitKeyGenerator(properties, rateLimitUtils) {
        @Override
        public String key(HttpServletRequest request, Route route, 
          RateLimitProperties.Policy policy) {
            return super.key(request, route, policy) + "_" + request.getMethod();
        }
    };
}

Вышеупомянутый код притяяяет имя метода REST к ключу. Например:

X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1_GET: 5

Другим ключевым моментом является то, что RateLimitKeyГенератор фасоль будет автоматически настроена весна-облако-zuul-ratelimit .

7. Обработка пользовательских ошибок

Структура поддерживает различные реализации для хранения данных ограничения скорости. Например, предоставляются Spring Data JPA и Redis. По умолчанию сбои просто регистрируются как ошибки с помощью По умолчаниюРейтерЭррорХэндлер класс.

Когда нам нужно обрабатывать ошибки по-разному, мы можем определить пользовательский RateLimiterErrorHandler боб:

@Bean
public RateLimiterErrorHandler rateLimitErrorHandler() {
    return new DefaultRateLimiterErrorHandler() {
        @Override
        public void handleSaveError(String key, Exception e) {
            // implementation
        }

        @Override
        public void handleFetchError(String key, Exception e) {
            // implementation
        }

        @Override
        public void handleError(String msg, Exception e) {
            // implementation
        }
    };
}

Похожи на RateLimitKeyГенератор фасоль, RateLimiterErrorHandler фасоль также будет автоматически настроена.

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

В этой статье мы увидели, как оценить ограничение API с помощью весеннего облака Netflix Зуул и весеннего облака Зуул RateLimit.

Как всегда, полный код этой статьи можно найти более на GitHub .