В скором времени - да. Мы сделали это, и это работает довольно хорошо.
Хитрость в том, что вы должны думать глубже, чем просто рабочие приложения API, когда дело доходит до горизонтального масштабирования. Если вам нужна push-архитектура , она должна быть асинхронной с самого начала.
Для этого мы использовали системы массового обслуживания , а именно RabbitMQ .
Представьте себе такой сценарий создания отчета, который может занять до 10 минут:
- Клиент подключается к нашему API GraphQL (экземпляр 1) через WebSocket
- Клиент отправляет команду на создание отчета через WebSocket
- API создает токен для команды и помещает команду для генерации отчета в CommandQueue (в RabbitMQ), возвращая токен Клиенту.
- Клиент подписывается на события результата своей команды, используя токен
- Какой-то бэкэнд-работник берет команду и выполняет процедуру генерации отчета
- В течение этого времени API GraphQL (экземпляр 1) умирает
- Клиент автоматически переподключается к GraphQL API (экземпляр 2)
- Клиент продлевает подписку с ранее приобретенным токеном
- Работник завершен, результаты в EventsQueue (RabbitMQ)
- ВСЕ наши экземпляры GraphQL получают информацию о
ReportGenerationDoneEvent
и проверяют, слушает ли кто-нибудь его токен.
- GraphQL API (экземпляр 2) видит, что Клиент ожидает результатов. Подталкивает результаты через веб-сокеты.
- API GraphQL (экземпляры 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.