Управление загрузкой с помощью PostgreSQL и pg_headerkit
Привет, Хабр!
Supabase — это крутой open-source аналог Firebase, с его помощью можно организовать крутые штуки вроде ограничения скорости запросов.
Supabase — это инструмент, который дает возможность создавать масштабируемые серверные решения, используя PostgreSQL. С его помощью можно легко управлять базами данных, аутентификацией, хранением данных и реальным временем, но без всяких vendor lock-ins.
Rate Limiting контролирует поток запросов, чтобы ваш сервер не ушел в нокаут от перегрузки. Это спасает сервера от DDOS-атакти помогает обеспечить более равномерное распределение ресурсов среди пользователей.
pg_headerkit
pg_headerkit — это расширение PostgreSQL, предназначенное для управления HTTP-заголовками прямо из вашей базы данных. Это расширение позволяет вам манипулировать HTTP-заголовками на уровне бдшки, что открывает широкие возможности для контроля и управления вашими API-запросами.
С pg_headerkit можно установить различные правила и логику для обработки входящих и исходящих HTTP-запросов. Например, вы можете настроить ограничение скорости на основе IP-адреса или API-ключа, а также управлять кэшированием или даже блокировать запросы, не соответствующие определенным критериям.
Для установки pg_headerkit потребуется иметь учетку supbase и установить dbdev на гитхабе.
Устанавливаем:
SELECT dbdev.install('burggraf-pg_headerkit');
CREATE EXTENSION "burggraf-pg_headerkit" VERSION '1.0.0';
Далее создадмим проект на официальном сайте Supabase.
Войдите в Supabase Dashboard. Откройте раздел SQL.
Запустим SQL-запрос для создания таблицы rate_limit
:
CREATE TABLE rate_limit (
api_key VARCHAR(255) NOT NULL,
last_request_time TIMESTAMP NOT NULL DEFAULT now(),
request_count INTEGER NOT NULL DEFAULT 1
);
Реализация ограничения скорости запросов
Входим в нашу БД постгреса и создаем расширение pg_headerkit:
psql -U username -d database
CREATE EXTENSION pg_headerkit;
Создадим таблицу, которая будет использоваться для контроля скорости запросов:
CREATE TABLE rate_limit (
api_key VARCHAR(255) NOT NULL,
last_request_time TIMESTAMP NOT NULL DEFAULT now(),
request_count INTEGER NOT NULL DEFAULT 1
);
Напишем функцию, которая будет проверять и обновлять счетчик запросов:
CREATE OR REPLACE FUNCTION check_rate_limit() RETURNS TRIGGER AS $$
DECLARE
time_difference INTERVAL;
BEGIN
-- Проверяем время с последнего запроса
SELECT now() - last_request_time INTO time_difference FROM rate_limit WHERE api_key = NEW.api_key;
IF time_difference > INTERVAL '1 minute' THEN
-- Сброс счетчика запросов после 1 минуты
UPDATE rate_limit SET request_count = 1, last_request_time = now() WHERE api_key = NEW.api_key;
ELSE
-- Увеличиваем счетчик запросов
UPDATE rate_limit SET request_count = request_count + 1 WHERE api_key = NEW.api_key;
IF (SELECT request_count FROM rate_limit WHERE api_key = NEW.api_key) > 10 THEN
-- Превышен лимит запросов
RAISE EXCEPTION 'Превышен лимит запросов';
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Cоздадим триггер, который будет вызывать нашу функцию при каждом запросе:
CREATE TRIGGER rate_limit_trigger
BEFORE INSERT ON your_table
FOR EACH ROW EXECUTE FUNCTION check_rate_limit();
Теперь каждый раз, когда кто-то пытается сделать запрос, наш триггер проверяет, не превышен ли лимит запросов, и если да, то выкидывает исключение.
Другие возможности
Можно настроить более гибкие ограничения скорости, которые будут меняться в зависимости от времени суток. Например, установить более жесткие ограничения в часы пик и более мягкие в нерабочее время:
CREATE OR REPLACE FUNCTION dynamic_rate_limit() RETURNS TRIGGER AS $$
DECLARE
current_hour INT;
BEGIN
current_hour := EXTRACT(HOUR FROM now());
IF current_hour BETWEEN 8 AND 18 THEN
-- Жесткие ограничения в рабочее время
ELSE
-- Мягкие ограничения в нерабочее времни]
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER dynamic_rate_limit_trigger
BEFORE INSERT ON your_table
FOR EACH ROW EXECUTE FUNCTION dynamic_rate_limit();
Можно создать более сложную систему лимитов, которая будет учитывать, к примеру, разные уровни доступа пользователей или их группы:
CREATE OR REPLACE FUNCTION user_specific_rate_limit() RETURNS TRIGGER AS $$
DECLARE
user_role VARCHAR;
BEGIN
SELECT role INTO user_role FROM users WHERE api_key = NEW.api_key;
IF user_role = 'premium' THEN
-- Высокие лимиты для премиум пользователей
ELSE
-- Стандартные лимиты для обычных пользователей
END IF;
-- [Остальная логика функции]
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Можно использовать pg_headerkit для добавления HTTP-заголовков в ответ, информирующих пользователя о текущем состоянии его лимитов:
CREATE OR REPLACE FUNCTION add_rate_limit_headers() RETURNS TRIGGER AS $$
DECLARE
remaining_requests INTEGER;
BEGIN
SELECT (10 - request_count) INTO remaining_requests FROM rate_limit WHERE api_key = NEW.api_key;
PERFORM headerkit.add('X-RateLimit-Remaining', remaining_requests::TEXT);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Если у вас есть список IP-адресов, с которых не должно быть доступа к вашему API, вы можете легко заблокировать их:
CREATE TABLE blocked_ips (
ip_address VARCHAR(15)
);
INSERT INTO blocked_ips (ip_address) VALUES ('192.168.1.1');
CREATE OR REPLACE FUNCTION check_blocked_ips() RETURNS TRIGGER AS $$
BEGIN
IF EXISTS (SELECT 1 FROM blocked_ips WHERE ip_address = NEW.ip_address) THEN
RAISE EXCEPTION 'Доступ запрещен с вашего IP-адреса';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER ip_block_trigger
BEFORE INSERT ON your_requests_table
FOR EACH ROW EXECUTE FUNCTION check_blocked_ips();
pg_headerkit только улучшит производительность баз данных за счет более гибкого и точного управления загрузкой, и обеспечивает возможность более тонкой настройки процессов в соответствии с потребностями конкретной системы.
Больше практических инструментов и навыков эксперты отрасли показывают в рамках онлайн-курса PostgreSQL для администраторов баз данных и разработчиков. Присоединяйтесь.