Я пытаюсь создать приложение, содержащее модуль обмена мгновенными сообщениями, и одна из основных задач - сохранить масштабируемость приложения независимо от количества пользователей или сообщений, которыми обмениваются.
В статья я читал, что можно создавать приложения в реальном времени с использованием GraphQL с «подписками», и в дополнение к этому, это простой в использовании протокол, а его преимущество сводится к минимуму извлечения объектов в оба конца и, следовательно, к меньшему использованию ресурсов.
Но что, если нам нужно добавить в систему новый сервер / узел для горизонтального масштабирования? Возможно ли это с помощью GraphQL?
В качестве примера реализации веб-сокетов, допускающих горизонтальное масштабирование, есть SocketCluster. Интересно, может ли приложение, разработанное только с помощью GraphQL, масштабироваться на нескольких узлах / машинах, или оно должно использоваться с другой структурой, такой как SocketCluster, для достижения этой цели.





Вкратце - да. Мы сделали это, и он работает очень хорошо.
Хитрость в том, что когда дело доходит до горизонтального масштабирования, вы должны думать глубже, чем просто рабочие приложения API. Если вы хотите толкать архитектуру, это должно быть асинхронный с самого начала.
Для этого мы использовали системы массового обслуживания, а именно RabbitMQ.
Представьте себе такой сценарий создания отчета, который может занять до 10 минут:
ReportGenerationDoneEvent и проверяют, прослушивает ли кто-нибудь его токен.ReportGenerationDoneEvent.Он довольно обширен, но с простыми абстракциями вам не нужно думать обо всей этой сложности и писать ~ 30 строк кода для нескольких служб для нового процесса, использующего этот маршрут.
И что в этом замечательного, вы получаете хорошее горизонтальное масштабирование, возможность повторного воспроизведения событий (повторные попытки), разделение проблем (клиент, api, рабочие), как можно быстрее отправляете данные клиенту, и, как вы упомянули, вы делаете не тратить пропускную способность на запросы are we done yet?.
Еще одна интересная вещь заключается в том, что всякий раз, когда пользователь открывает список отчетов на нашей панели, он видит текущие генерируемые отчеты и может подписаться на их изменения, поэтому им не нужно обновлять список вручную.
Хорошие мысли о SocketCluster. Это позволит оптимизировать шаг 10 в приведенном выше сценарии, но на данный момент мы не видим никаких проблем с производительностью при широковещательной передаче ReportGenerationDoneEvent на весь кластер API. При большем количестве экземпляров или многорегиональной архитектуре это было бы необходимо, поскольку это позволило бы улучшить масштабирование и сегментирование.
Важно понимать, что SocketCluster работает на уровне взаимодействия (WebSockets), но уровень логического API (GraphQL) находится над ним. Чтобы оформить подписку GraphQL, вам просто нужно использовать протокол связи, который позволяет передавать информацию пользователю, а WebSockets позволяет это.
Я думаю, что использование SocketCluster - хороший выбор для дизайна, но не забудьте повторить итерацию с реализацией. Используйте SocketCluster только тогда, когда вы планируете открыть много сокетов в любой момент времени. Кроме того, вы должны подписываться только при необходимости, потому что WebSocket отслеживает состояние и требует управления и пульса.
Если вас также интересует асинхронная серверная архитектура, которую я использовал выше, прочитайте о шаблонах CQRS и Event Sourcing.
Когда вы настраиваете свой проект для использования WebSockets, вы теоретически можете отказаться от всех HTTP-запросов GraphQL и просто использовать WebSockets для связи. Чтобы оформить подписку GraphQL, вам просто нужно использовать протокол связи, который позволяет передавать информацию пользователю, а WebSockets позволяет это. Таким образом, они не эквивалентны, они работают на разных уровнях - подписка GQL находится на логическом уровне более высокого уровня и использует WebSocket для передачи данных. Кроме того, использование SocketCluster в основном даст вам то, что вы только что сказали - масштабирование и сегментирование.
В комбинированном дизайне REST и Websockets (см. Вопрос stackoverflow.com/a/43971625/5486116) многие рекомендуют реализовать решение, в котором мы подписываемся на изменения только в случае необходимости. Я думаю, что это решение потребляет меньше ресурсов и дает лучшую производительность ... что, если мы хотим применить ту же логику при объединении веб-сокетов с GraphQL: мы общаемся в основном через Websocket (SocketCluster), и для любого обмена данными мы используем GraphQL, и Конечно, SocketCluster будет в основном отвечать за масштабируемость и сегментирование. Это хороший дизайн?
Я думаю, что это хороший выбор дизайна, но не забудьте повторить итерацию с реализацией. Используйте SocketCluster только тогда, когда вы планируете открыть много сокетов в любой момент времени. Также да, вы должны подписываться только при необходимости, потому что WebSocket отслеживает состояние и требует управления и пульса.
Еще раз спасибо за ваше руководство. Что вы думаете о добавлении резюме к вашему ответу, чтобы больше людей могли извлечь пользу из того, что вы мне рассказали в своих комментариях?
Спасибо за ваш ответ. Просто чтобы хорошо понять: когда вы говорите об использовании GraphQL API через Websocket, вы имеете в виду, что вы инициируете соединение с сокетом через Websocket, а затем обрабатываете обмен данными с GraphQL? Я все еще новичок в этих технологиях, поэтому, пожалуйста, поправьте меня, если я ошибаюсь: не эквивалентны ли подписки GraphQL и соединения WebSocket? В чем преимущество использования GraphQL с SocketCluster, если не для масштабирования и сегментирования?