У нас есть ситуация, когда мне приходится иметь дело с огромным потоком событий, поступающих на наш сервер, в среднем около 1000 событий в секунду (пик может составлять ~ 2000).
Проблема
Наша система размещена на Heroku и использует относительно дорогую базу данных Heroku Postgres , которая позволяет подключать до 500 БД. Мы используем пул соединений для подключения с сервера к БД.
События приходят быстрее, чем может обработать пул соединений с БД
Проблема в том, что события происходят быстрее, чем может обработать пул соединений. К тому времени, когда одно соединение завершило передачу по сети от сервера к БД, чтобы оно могло быть возвращено обратно в пул, n
приходит больше дополнительных событий.
В конечном итоге события складываются в ожидании сохранения и, поскольку в пуле нет доступных подключений, они истекают, и вся система становится неработоспособной.
Мы решили аварийную ситуацию, отправляя оскорбительные высокочастотные события медленнее от клиентов, но мы все еще хотим знать, как обращаться с этими сценариями в случае, если нам нужно обрабатывать эти высокочастотные события.
Ограничения
Другие клиенты могут захотеть читать события одновременно
Другие клиенты постоянно запрашивают чтение всех событий с определенным ключом, даже если они еще не сохранены в БД.
Клиент может запросить GET api/v1/events?clientId=1
и получить все события, отправленные клиентом 1, даже если эти события еще не сохранены в БД.
Есть ли "классные" примеры того, как с этим бороться?
Возможные решения
Поставьте в очередь события на нашем сервере
Мы можем поставить в очередь события на сервере (с максимальным параллелизмом в очереди, равным 400, чтобы пул соединений не заканчивался).
Это плохая идея, потому что:
- Это съест доступную память сервера. Сложенные в очередь события будут занимать огромное количество оперативной памяти.
- Наши серверы перезагружаются один раз каждые 24 часа . Это жесткий предел, наложенный Heroku. Сервер может перезапускаться, пока события ставятся в очередь, что приводит к потере событий в очереди.
- Он вводит состояние на сервере, что ухудшает масштабируемость. Если у нас настроена многосерверная система, и клиент хочет прочитать все события + в очереди + сохраненные, мы не будем знать, на каком сервере находятся события в очереди.
Используйте отдельную очередь сообщений
Я предполагаю, что мы могли бы использовать очередь сообщений (например, RabbitMQ ?), Где мы перекачиваем в нее сообщения, а на другом конце есть другой сервер, который занимается только сохранением событий в БД.
Я не уверен, позволяют ли очереди сообщений запрашивать события в очереди (которые еще не были сохранены), поэтому, если другой клиент хочет прочитать сообщения другого клиента, я могу просто получить сохраненные сообщения из БД и ожидающие сообщения из очереди и объединить их вместе, чтобы я мог отправить их обратно клиенту запроса на чтение.
Используйте несколько баз данных, каждая из которых сохраняет часть сообщений на центральном сервере БД-координатор для управления ими
Другое решение, которое у нас есть, - это использование нескольких баз данных с центральным «координатором БД / балансировщиком нагрузки». Получив событие, этот координатор выберет одну из баз данных для записи сообщения. Это должно позволить нам использовать несколько баз данных Heroku, тем самым увеличивая ограничение на соединение до 500 x количество баз данных.
По запросу на чтение этот координатор может выдавать SELECT
запросы к каждой базе данных, объединять все результаты и отправлять их обратно клиенту, который запросил чтение.
Это плохая идея, потому что:
- Эта идея звучит как ... хм .. чрезмерная инженерия? Было бы кошмаром управлять также (резервное копирование и т. Д.). Его сложно построить и поддерживать, и если это не является абсолютно необходимым, это звучит как нарушение KISS .
- Он жертвует последовательностью . Делать транзакции между несколькими БД не стоит, если мы пойдем с этой идеей.
ANALYZE
запросы, и они не являются проблемой. Я также создал прототип для проверки гипотезы пула соединений и убедился, что это действительно проблема. База данных и сам сервер живут на разных машинах, следовательно, задержка. Кроме того, мы не хотим отказываться от Heroku, если в этом нет абсолютной необходимости, и для нас огромные плюсы - не беспокоиться о развертывании .
select null
на 500 подключений. Бьюсь об заклад, вы обнаружите, что пул соединений не проблема там.