Горизонтальное масштабирование и GraphQL в среде Node.js

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

В статье я прочитал, что можно создавать приложения в реальном времени с использованием GraphQL с "подписками", и в дополнение к этому, он прост в использовании protocole и имеет преимущество в том, что сводит к минимуму извлечение объектов в оба конца и, следовательно, использует меньше ресурсов.

Но что, если нам нужно добавить новый сервер / узел в систему для горизонтального масштабирования? Возможно ли это с помощью GraphQL?

В качестве примера реализации веб-сокетов, которая позволяет горизонтальное масштабирование, представлен SocketCluster. Интересно, может ли приложение, разработанное одним только GraphQL, быть масштабируемым по нескольким узлам / машинам, или оно должно использоваться с другой средой, такой как SocketCluster, для достижения этой цели.

1 ответ

Решение

В скором времени - да. Мы сделали это, и это работает довольно хорошо.

Хитрость в том, что вы должны думать глубже, чем просто рабочие приложения API, когда дело доходит до горизонтального масштабирования. Если вы хотите использовать push-архитектуру, она должна быть асинхронной с самого начала.

Для этого мы использовали системы массового обслуживания, а именно RabbitMQ.

Представьте себе такой сценарий создания отчета, который может занять до 10 минут:

  1. Клиент подключается к нашему API GraphQL (экземпляр 1) через WebSocket
  2. Клиент отправляет команду на создание отчета через WebSocket
  3. API генерирует токен для команды и помещает команду для генерации отчета в CommandQueue (в RabbitMQ), возвращая токен Клиенту.
  4. Клиент подписывается на события своего командного результата, используя токен
  5. Какой-то бэкэнд-работник выбирает команду и выполняет процедуру генерации отчета
  6. За это время GraphQL API (экземпляр 1) умирает
  7. Клиент автоматически переподключается к GraphQL API (экземпляр 2)
  8. Клиент продлевает подписку с ранее приобретенным токеном
  9. Работник сделан, результаты на EventsQueue (RabbitMQ)
  10. ВСЕ наши экземпляры GraphQL получают информацию о ReportGenerationDoneEvent и проверьте, слушает ли кто-нибудь его токен.
  11. GraphQL API (экземпляр 2) видит, что Клиент ожидает результатов. Подталкивает результаты через веб-сокеты.
  12. GraphQL API (экземпляры 3-100) игнорируют ReportGenerationDoneEvent,

Он довольно обширный, но с простыми абстракциями вам не нужно думать обо всей этой сложности и писать ~30 строк кода в нескольких сервисах для нового процесса, использующего этот маршрут.

И что самое замечательное в этом, вы получаете хорошее горизонтальное масштабирование, повторяемость событий (повторные попытки), разделение проблем (клиент, API, рабочие), как можно быстрее отправляете данные клиенту, и, как вы упомянули, вы делаете не тратить трафик на are we done yet? Запросы.

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

Хорошие размышления о SocketCluster. Это оптимизировало бы шаг 10 в вышеприведенном сценарии, но пока мы не видим проблем с производительностью при трансляции ReportGenerationDoneEvent на весь кластер API. При наличии большего количества экземпляров или многорегиональной архитектуры это будет необходимо, так как это позволит улучшить масштабирование и разделение.

Важно понимать, что SocketCluster работает на уровне связи (WebSockets), но уровень логического API (GraphQL) выше этого уровня. Чтобы создать подписку GraphQL, вам просто нужно использовать протокол связи, который позволяет передавать информацию пользователю, а WebSockets позволяет это.

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

Если вас больше интересует асинхронная архитектура бэкенда, которую я использовал выше, ознакомьтесь с шаблонами CQRS и Event Sourcing.

Другие вопросы по тегам