Имеем HTTP-API на основе aiohttp.
В prod-режиме это приложение запущено в несколько процессов, поэтому учитывать запросы пользователей будем в общей для всех процессов базе Redis.
При каждом запросе ID пользователя передаётся в функцию rate_exceeded
из app/redis.py
.
Можно задать любой предел частоты и интервал учёта запросов. Их значения по-умолчанию:
разрешено 10 запросов в секунду, учитываются запросы не далее 1 секунды назад.
docker compose up
...
docker compose stop
docker compose rm
Необходим запущенный локально Redis-сервер и виртуальное окружение Python:
python3 -m venv venv
. venv/bin/activate
(venv) pip install aiohttp redis
(venv) REDIS_URL=redis://localhost/0 python3 -m app
...
(venv) deactivate
Возьмём современный инструмент для нагрузочного тестирования Grafana k6. Нагрузим API параллельными HTTP-запросами 30 секунд:
$ k6 run ./k6/get.js
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: ./k6/get.js
output: -
scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration (incl. graceful stop):
* default: 10 looping VUs for 30s (gracefulStop: 30s)
200 OK.........................: 300 9.998438/s
429 Too Many Requests..........: 56317 1876.940127/s
...
Видим, что за 30 секунд успешно прошло ровно 300 запросов, остальные были отброшены. Это точно соответствует заданному в API ограничению: 10 запросов в секунду.