Некоторое время назад для своего проекта я искал хороший сервис, ограничивающий ставку. Для масштаба этого проекта служба будет работать вдоль переднего прокси и будет привлекать к ответственности запросы на сторонние заявки.
Nginx Plus и Kong, безусловно, имеют ограничивающие скорость особенности, но не OSS; Пока я более крупный поклонник OSS. Использование сервисной сетки iStio было бы излишним. Поэтому я решил использовать Envoy Proxy + Lyft RateLimiting Анкет
Цель этого блога-помочь вам начать работу с ограничивающим ставку службой и настраивать различные комбинации сценариев, ограничивающих скорость.
Давайте погрузимся в…
Конфигурация ratelimit состоит из
1. Домен : Домен — это контейнер для набора пределов скорости. Все домены, известные услуге RateLimit, должны быть глобально уникальными. Они служат способом для различных команд/проектов иметь ограничение на скорость конфигурации, которые не конфликтуют.
2. Дескриптор : Дескриптор — это список паров ключей/значения, принадлежащих домену, который использует служба RateLimit для выбора правильного ограничения скорости. Дескрипторы чувствительны к случаям. Примеры дескрипторов: * («База данных», «Пользователи») * («message_type», «Marketing»), («to_number», «2061234567») * («to_cluster», «service_a») * («to_cluster», «service_a»), («from_cluster», «service_b»)
Дескрипторы также могут быть вложены для достижения более сложных сценариев ограничивающих ставок.
Мы будем выполнять ограничение скорости на основе различных заголовков HTTP. Давайте посмотрим на файл конфигурации.
domain: apis descriptors: - key: generic_key value: global rate_limit: unit: second requests_per_unit: 60 - key: generic_key value: local rate_limit: unit: second requests_per_unit: 50 - key: header_match value: "123" rate_limit: unit: second requests_per_unit: 40 - key: header_match value: "456" rate_limit: unit: second requests_per_unit: 30 - key: header_match value: post rate_limit: unit: second requests_per_unit: 20 - key: header_match value: get rate_limit: unit: second requests_per_unit: 10 - key: header_match value: path rate_limit: unit: second requests_per_unit: 5 #Using nested descriptors - key: custom_header descriptors: - key: plan value: BASIC rate_limit: requests_per_unit: 2 unit: second - key: plan value: PLUS rate_limit: requests_per_unit: 3 unit: second
В приведенной выше конфигурации можно ясно видеть
1. Есть разные ключи с различными значениями RateLimit. 2. Мы можем использовать их во всем мире для всего Vhost в посланнике или даже на местном уровне для конкретного пути. 3. Мы также можем иметь вложенные значения дескрипторов.
Давайте посмотрим, как мы можем использовать их в конфигурации Envoy. Посмотрите на конфигурацию ниже:
static_resources: listeners: - name: listener_0 address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.http_connection_manager config: stat_prefix: ingress_http codec_type: AUTO route_config: name: local_route virtual_hosts: - name: nginx domains: - "*" rate_limits: - stage: 0 actions: - generic_key: descriptor_value: "global" routes: - match: prefix: "/nginx_1" route: cluster: nginx_1 include_vh_rate_limits: true rate_limits: - actions: - generic_key: descriptor_value: "local" - actions: - header_value_match: descriptor_value: "get" headers: - name: ":method" prefix_match: "GET" - actions: #This will be triggered if `X-CustomHeader` is present AND the X-CustomPlan header has a value of either BASIC or PLUS - requestHeaders: descriptor_key: "custom_header" header_name: "X-CustomHeader" - requestHeaders: descriptor_key: "plan" header_name: "X-CustomPlan" - match: prefix: "/nginx_2" route: cluster: nginx_2 include_vh_rate_limits: true rate_limits: - actions: - generic_key: descriptor_value: "local" - actions: - header_value_match: descriptor_value: "123" headers: - name: "X-MyHeader" prefix_match: "123" - actions: - header_value_match: descriptor_value: "456" headers: - name: "X-MyHeader" prefix_match: "456" - actions: - header_value_match: descriptor_value: "post" headers: - name: ":method" prefix_match: "POST" - actions: - header_value_match: descriptor_value: "path" headers: - name: ":path" prefix_match: "/nginx" http_filters: - name: envoy.rate_limit config: domain: apis failure_mode_deny: false rate_limit_service: grpc_service: envoy_grpc: cluster_name: rate_limit_cluster timeout: 0.25s - name: envoy.router clusters: - name: nginx_1 connect_timeout: 1s type: strict_dns lb_policy: round_robin hosts: - socket_address: address: nginx1 port_value: 80 - name: nginx_2 connect_timeout: 1s type: strict_dns lb_policy: round_robin hosts: - socket_address: address: nginx2 port_value: 80 - name: rate_limit_cluster type: strict_dns connect_timeout: 0.25s lb_policy: round_robin http2_protocol_options: {} hosts: - socket_address: address: ratelimit port_value: 8081 admin: access_log_path: "/dev/null" address: socket_address: address: 0.0.0.0 port_value: 9901
Вот как это работает:
1. Мы определили Одиночный Vhost названный nginx который соответствует всем доменам. 2. Для этого Vhost определяется глобальная ограничение ставки. Значение дескриптора — глобальное Анкет 3.next, у нас есть 2 кластера Под этим Vhost. А именно, nginx1 и nginx2 Анкет Маршруты для PATH/NGINX1 направляются в nginx1 кластер и аналогично Nginx2. 4. Для Nginx1 существует общий предел скорости, определяемый значением дескриптора локальным И тогда у нас есть ограничения по скорости для разных значений Стандартные заголовки HTTP, такие как метод, путь и т. Д., И некоторые пользовательские заголовки HTTP, такие как X-Customheader Анкет 5. У нас есть аналогичный набор ограничивающих скоростей для кластера Nginx2. 6. Эти 2 кластеры Nginx, определенные здесь, на самом деле относятся к 2 различным контейнерам Nginx, работающим в рамках стека Docker-Compose.
Общая архитектура может быть визуализирована как
Все файлы конфигурации для этой настройки можно найти Здесь Анкет Поскольку они работают в той же сети, что и прокси -сервер RateLimiter и Envoy, к ним можно легко получить доступ, используя имя контейнера.
Чтобы запустить настройку, просто клонировать репо и
docker-compose up
Как только стек встал, у вас будет
1.2 nginx контейнеры, работающие на порту 9090
и 9091
местный хост. 2. Прокси -сервера для перехвата и ретрансляции на серверы Nginx. Консоль администратора Endoy может быть достигнута в Localhost: 9901
Анкет 3.envoy будет слушать как Localhost: 10000
Анкет 4. Служебный контейнер с настройками с настройками скорости, которые будут использоваться для послужного заведения. 5. Redis Container, который используется услугами RateLimiting.
Важно понимать, что все применимые действия для конкретного пути в кластере агрегированы Ratelimiter для результата, то есть
Логично ИЛИ
из всех применимых пределов
Во -первых, нам нужно установить Вегета , структура нагрузочного тестирования. Это может быть сделано
brew update && brew install vegeta
Тестовый сценарий 1
Случай : Получить
запрос на /nginx_1/
по 100 запросам в секунду ожидаемый результат : 10% запросов успешно. (Логично Или
из "descriptor_value"
: "Global"
, "descriptor_value": "Local"
и "descriptor_value": "Get"
) Команда : Echo "Get http://localhost: 10000/nginx_1/" | Вегета атака | Вегета отчет
Фактический результат
$ echo "GET http://localhost:10000/nginx_1/" | vegeta attack -rate=100 -duration=0 | vegeta report Requests [total, rate, throughput] 1008, 100.12, 10.92 Duration [total, attack, wait] 10.071526192s, 10.06832056s, 3.205632ms Latencies [mean, 50, 95, 99, max] 4.718253ms, 4.514212ms, 7.426103ms, 9.089064ms, 17.916071ms Bytes In [total, mean] 68640, 68.10 Bytes Out [total, mean] 0, 0.00 Success [ratio] 10.91% Status Codes [code:count] 200:110 429:898 Error Set: 429 Too Many Requests
— —
Тестовый сценарий 2 :
Случай : Пост
запрос на /nginx_1/
при 100 запросах в секунду. WederSult : 50% запросов успешно. (Логично Или
из "descriptor_value"
: "Global"
и "descriptor_value": "Local"
) Команда : Echo "post http://localhost: 10000/nginx_1/" | Вегета атака | Вегета отчет
Фактическое развлечение :
$ echo "POST http://localhost:10000/nginx_1/" | vegeta attack -rate=100 -duration=0 | vegeta report Requests [total, rate, throughput] 4344, 100.02, 50.56 Duration [total, attack, wait] 43.434227783s, 43.429286664s, 4.941119ms Latencies [mean, 50, 95, 99, max] 5.190485ms, 5.224978ms, 7.862512ms, 10.340628ms, 20.573212ms Bytes In [total, mean] 1370304, 315.45 Bytes Out [total, mean] 0, 0.00 Success [ratio] 50.55% Status Codes [code:count] 200:2196 429:2148 Error Set: 429 Too Many Requests
— —
Тестовый сценарий 3 :
Случай : Получить
запрос на /nginx_2/
по 100 запросам в секунду с X-Myheader: 123
Ожидаемый результат : 5% запросов успешно (логично Или
из "descriptor_value"
: "Global"
, "descriptor_value": "Local"
, "descriptor_value": "123"
и "descriptor_value": "path"
) Команда : Echo "Get http://localhost: 10000/nginx_2/" | Вегета атака -Хедер "X -Myheader: 123" | Вегета отчет
Фактический результат :
$ echo "GET http://localhost:10000/nginx_2/" | vegeta attack -rate=100 -duration=0 -header "X-MyHeader: 123" | vegeta report Requests [total, rate, throughput] 3861, 100.03, 5.18 Duration [total, attack, wait] 38.604406747s, 38.597776398s, 6.630349ms Latencies [mean, 50, 95, 99, max] 4.96685ms, 4.673049ms, 7.683458ms, 9.713522ms, 16.875025ms Bytes In [total, mean] 124800, 32.32 Bytes Out [total, mean] 0, 0.00 Success [ratio] 5.18% Status Codes [code:count] 200:200 429:3661 Error Set: 429 Too Many Requests
— — — Тестовый сценарий 4 :
Случай : Пост
запрос на /nginx_2/
по 100 запросам в секунду с X-Myheader: 456
Ожидаемый результат : 5% запросов успешно (логично Или
из "descriptor_value": "Global"
, "descriptor_value": "Local"
, "descriptor_value": "post"
, "descriptor_value": "456"
и "descriptor_value": "path"
) Команда : Echo "post http://localhost: 10000/nginx_2/" | Вегета атака -Хедер "X -Myheader: 456" | Вегета отчет
Фактический результат :
$ echo "POST http://localhost:10000/nginx_2/" | vegeta attack -rate=100 -duration=0 -header "X-MyHeader: 456" | vegeta report Requests [total, rate, throughput] 2435, 100.04, 5.13 Duration [total, attack, wait] 24.346703709s, 24.339554311s, 7.149398ms Latencies [mean, 50, 95, 99, max] 5.513994ms, 5.255698ms, 8.239173ms, 10.390515ms, 20.287931ms Bytes In [total, mean] 78000, 32.03 Bytes Out [total, mean] 0, 0.00 Success [ratio] 5.13% Status Codes [code:count] 200:125 429:2310 Error Set: 429 Too Many Requests
— — — Тестовый сценарий 5 : Случай : Получить
запрос на /nginx_1/
по 100 запросам в секунду с X-Customheader: xyz
и X-custplan: Плюс
Ожидаемый результат : 3% запросов успешно (логично Или
из "descriptor_value": "Global"
, "descriptor_value": "Local"
, "descriptor_value": "Get"
, "descriptor_key": "custom_header"
и "descriptor_key": "план"
) Команда : Echo "Get http://localhost: 10000/nginx_1/" | Вегета атака -Хедер "X -Customheader: xyz" -Header "x -custplan: плюс" | Вегета отчет
Фактический результат :
$ echo "GET http://localhost:10000/nginx_1/" | vegeta attack -rate=100 -duration=0 -header "X-CustomHeader: XYZ" -header "X-CustomPlan: PLUS" | vegeta report Requests [total, rate, throughput] 2372, 100.05, 3.16 Duration [total, attack, wait] 23.71156424s, 23.707710415s, 3.853825ms Latencies [mean, 50, 95, 99, max] 5.396743ms, 5.179951ms, 7.981171ms, 10.084753ms, 15.086778ms Bytes In [total, mean] 46800, 19.73 Bytes Out [total, mean] 0, 0.00 Success [ratio] 3.16% Status Codes [code:count] 200:75 429:2297 Error Set: 429 Too Many Requests
— — — Тестовый сценарий 6 : Случай : Получить
запрос на /nginx_1/
по 100 запросам в секунду с X-Header: xyz и x-custmplan: Плюс
Ожидаемый результат : 10% запросов успешно (логично Или
из "descriptor_value": "Global"
, "descriptor_value": "Local"
, "descriptor_value": "Get"
) Команда : Echo "Get http://localhost: 10000/nginx_1/" | Вегета атака -Хедер "x -Header: xyz" -header "x -custplan: плюс" | Вегета отчет
Фактический результат:
$ echo "GET http://localhost:10000/nginx_1/" | vegeta attack -rate=100 -duration=0 -header "X-Header: XYZ" -header "X-CustomPlan: PLUS" | vegeta report Requests [total, rate, throughput] 1578, 100.07, 10.78 Duration [total, attack, wait] 15.773500478s, 15.769158977s, 4.341501ms Latencies [mean, 50, 95, 99, max] 4.748516ms, 4.514902ms, 7.076671ms, 8.518779ms, 16.077828ms Bytes In [total, mean] 106080, 67.22 Bytes Out [total, mean] 0, 0.00 Success [ratio] 10.77% Status Codes [code:count] 200:170 429:1408 Error Set: 429 Too Many Requests
В сценарии производства вы можете запустить несколько экземпляров вашего прокси, которые могут относиться к одному и тому же кластеру RateLimit. Передняя прокси в основном без сохранения состояния.
Что касается услуги RateLimiting, я бы порекомендовал масштабировать его горизонтально и перемещать кэш Redis в облачный сервис, такой как Redislabs или AWS Elastic-Cache.
Кроме того, используя отдельное Redis за пределы в секунду настоятельно рекомендуется. Все, что вам нужно сделать, это:
- Установите Env var,
Redis_persecond
:"истинный"
- Установить Redis Endpoint,
Redis_persecond_url
Мы можем ясно видеть, что фактические результаты довольно близки к ожидаемым результатам. Мы можем выполнить все виды сложных сценариев, ограничивающих скорость, используя это и выполнить запрос дросселирования.
Оригинал: «https://dev.to/appfleet/http-throttling-using-lyft-global-ratelimiting-4c5i»