Какие подходы и лучшие практики следует учитывать при проектировании API, которое будет обрабатывать большую нагрузку?

Каковы ключевые моменты, на которые стоит обратить внимание при проектировании высоконагруженного API, к которому будут постоянно обращаться партнерские сервисы? Какие потенциальные узкие места могут возникнуть в этом процессе? Я ищу рекомендации как по кодированию, так и по DevOps. В данный момент у меня есть несколько идей: 

1. Создать один сервер с несколькими инстансами API, где запросы будут распределяться балансировщиком pm2 для повышения надежности работы при сбое одного из инстансов.
2. Использовать Redis для кэширования проверок, чтобы минимизировать нагрузку на базу данных.
3. Использовать Redis для маркировки выполненных операций, чтобы исключить повторные запросы.
4. Записывать данные не в основную базу данных платформы, а во временную базу API, с последующей периодической выгрузкой данных в основную базу.

Могли бы вы предложить дополнительные рекомендации или указать на недостатки в предложенных подходах?
  • 6 сентября 2024 г. 15:03
Ответы на вопрос 3
Проектирование API для обработки высокой нагрузки требует комплексного подхода, учитывающего как архитектурные решения, так и практики разработки и операций. Давайте рассмотрим ваши идеи и предложим дополнительные рекомендации.

### Ваши идеи и предложения:

1. **Балансировка нагрузки**: Применение балансировщика, такого как PM2, — это хорошая идея, но стоит рассмотреть возможность использования более специализированных решений (например, Nginx или HAProxy) для распределения нагрузки между инстансами API. Также важно настроить автоскейлинг для автоматического добавления инстансов при увеличении нагрузки.

2. **Кэширование с использованием Redis**: Кэширование — это отличный способ снизить нагрузку на базу данных. Однако следует чётко управлять жизненным циклом кэша, чтобы избежать устаревших данных. Можно также рассмотреть использование кэша на уровне приложения (например, memcached) и кэширования на уровне CDN для статики.

3. **Маркировка выполненных операций с помощью Redis**: Это хорошая практика для предотвращения повторных запросов. Не забудьте учитывать механизмы очистки кэша, чтобы избежать накопления устаревших записей.

4. **Запись данных во временную базу**: Этот подход может быть полезным, однако следует внимательно продумать механизм синхронизации с основной базой данных и обеспечить надежную обработку ошибок. Возможно, стоит использовать очереди (например, RabbitMQ или Kafka) для асинхронной обработки.

### Дополнительные рекомендации:

1. **Использование рейт-лимитинга**: Для защиты API от злоупотреблений и DDoS-атак можно реализовать рейт-лимитинг. Это поможет контролировать количество запросов от пользователей и избежать излишней нагрузки.

2. **График обслуживания и резервирование ресурсов**: Разработайте график обслуживания и выделите резервные мощности для критических операций, чтобы убедиться, что они будут доступные при пиковой нагрузке.

3. **Логирование и мониторинг**: Внедрите систему мониторинга (например, Prometheus и Grafana) для отслеживания производительности и состояния агрегируемых инстансов. Используйте логирование для анализа производительности и выявления узких мест.

4. **Разделение кода**: Рассмотрите возможность разделения микросервисов для разных аспектов API (например, авторизация, обработка запросов и работа с БД). Это обеспечит независимое масштабирование сервисов.

5. **Обработка ошибок и отказоустойчивость**: Внедрите стратегии обработки ошибок и retries для критических операций, а также тестируйте ваше приложение на вспомогательных сценариях (например, сетевые сбои).

6. **Оптимизация запросов к базам данных**: Проанализируйте и оптимизируйте запросы к вашим базам данных. Индексы и оптимизация запросов могут значительно снизить нагрузку.

7. **Документирование API**: Используйте инструменты, такие как Swagger/OpenAPI, для документирования вашего API. Это поможет вашим партнёрским сервисам легче интегрироваться с вашим API и понять его особенности.

### Потенциальные узкие места:

- **Базовые операции с данными**: Для записи и чтения данных из базы данных могут возникнуть узкие места, если не оптимизировать запросы.
- **Время отклика кэша**: Если Redis начнёт перегружаться, это может привести к увеличению времени отклика API.
- **Программные ошибки**: В случае ошибки в коде, которая приводит к долгим задержкам, это может повлиять на все инстансы API.
  
Проектирование высоконагруженного API требует внимания к деталям и гибкости, чтобы быть готовым к изменениям нагрузки и требованиям. Подходите к разработке системно и используйте возможность тестирования под нагрузкой для выявления проблемных зон.
Ты не сообщил самое главное - как будут читаться собираемые данные. Будут ли они считываться и тем более фильтроваться в процессе записи, можно ли вводить запаздывание при чтении данных (например до 'некоторых' данных в прошлом). 

Если ничего этого нет, то ничего между базой данных и бакэндом ставить не нужно... таблицы, в которые складываются данные должны быть без индексов, можно ввести искусственное партицирование, например таблица без индексов - последние не обработанные данные, вторая таблица - данные с индексами в которых будет проводиться поиск и анализ, для размазывания нагрузки использовать штатную репликацию базы данных, разные ноды - разные задачи. Кстати один из способов партицирования - писать данные блоками, каждый блок в свою новую таблицу, количество таблиц сравнимо с количеством нод, обрабатывающих их данные (таким образом можно отключить даже транзакции, ведь пока обрабатывается таблица, данные пусть пишутся в следующую, управление таблицами вести тут же на таблицах но уже с транзакциями)

Проблема не столько в данных и в их объеме, а в надежности всей схемы, т.е. например можно не делать единую точку отказа и сделать несколько независимых api endpoint, клиенты должны сами переключаться между ними, при ошибках, ну а сам выбор куда подключаться делать случайным.

Кстати, собственно сбор оперативных данных не обязательно делать в ОДНОЙ физической базе, это могут быть разные БД, а вот последующий анализ уже делать следующим сервисом (так же может быть несколько нод), собирающим данные из разных первичных источников в какой то единый или еще в какой то форме... именно подготовка данных к последующему их использованию и есть вопрос реализации.

Настоятельно не советую городить зоопарк из разных баз данных типа редис и sql-db.. когда sql база используется без индексов (и тем более без транзакций) на последовательную запись у нее нет конкурентов (ну только что совсем низкоуровневым программированием заняться)
Писать все данные не в основную бд (бд платформы), а во временную бд апи и раз в N минут/часов сгружать данные в основную бд
Гораздо проще использовать нормальный сервер очередей. Вдобавок, данные будут быстрее попадать в БД не вися в отстойнике.
Похожие вопросы