Некоторое время назад для своего проекта я искал хороший сервис, ограничивающий ставку. Для масштаба этого проекта служба будет работать вдоль переднего прокси и будет привлекать к ответственности запросы на сторонние заявки.
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»