Firebase больше не тянет: как мы строили real-time-чаты на Centrifugo

Привет, Хабр! Меня зовут Илья Логинов, я Chats Team Lead в маркетплейсе «Флаувау». Расскажу, как мы отказались от внешних серверов, безболезненно пережили переезд, избежали сбоев в высокий сезон и сократили расходы на эксплуатацию чатов в 100 (сто!) раз. Эта история — о нашем пути от Firebase к Centrifugo.
Что не так с облаком, когда масштабы и нагрузка растут
Когда бизнес растет, внутренние чаты из просто необходимого превращаются в критически важный инструмент. От них зависит, как быстро cеллер и покупатель обсудят детали, а значит, — состоится ли заказ. Изначально для чатов мы использовали Firebase, но со временем это решение перестало справляться с нашими аппетитами.
Стало очевидно, что оно просто не создано для хранения и обработки такого массива данных, какой генерировали мы. Только представьте: в наши пиковые даты — 14 февраля, 8 Марта, День матери — к Firebase было около 60 тысяч одновременных подключений. В такие дни до 4% запросов к чатам стабильно отваливались с ошибкой. Для нас это означало не просто сбой, а реальные простои и потерю денег.

Одна из главных причин, по которой пришлось задуматься о переезде, — мы банально уперлись в потолок самого Firebase. Организация данных в формате JSON существенно затрудняла любые манипуляции с историей сообщений или их обработку (и добавляла головной боли). Что-то поменять, проанализировать или просто почистить требовало непропорционально много усилий. Было ясно: с такой архитектурой данных о дальнейшем масштабировании можно забыть.

Также одним из камней преткновения стала стоимость обслуживания в Firebase. Каждый запрос пользователя к чату, будь то отправка или получение сообщения, был платным. Соответственно, на наших объемах трафика счета откровенно перестали радовать.

Пазл сложился. Пора было искать альтернативу — платформу, которая смогла бы обеспечить устойчивость, гибкость управления данными и снижение затрат на следующих этапах роста маркетплейса.
Как мы искали стабильное real-time-решение «на вырост»
После решения уйти с Firebase следующим шагом стал выбор инструмента, который закроет наши потребности. Главный вопрос был: чем обеспечить тот самый real-time? Как мгновенно оповещать всех участников чата о новых сообщениях, создании диалога или его архивации?
У нас было несколько важных требований к новому техническому решению: персональные каналы, авторизация, выдерживание высоких нагрузок и возможность переключения с Firebase на новые чаты и обратно. Последнее нам нужно было для того, чтобы, во-первых, предусмотреть бесшовный переход, а во-вторых — подстраховать себя на случай проблем с новыми чатами.
Мы детально прорабатывали процесс переключения и rollback между двумя стеками, чтобы минимизировать любое потенциальное время простоя и не потерять события чата на любом этапе перехода.
Рассматривались разные решения на базе WebSocket, в том числе популярный Socket.IO. Но, честно говоря, эти технологии показались нам уже приветом из прошлого и требовали бы серьезной доработки напильником. Особенно смущало отсутствие готовой кластеризации и масштабирования из коробки.
Centrifugo оказался самым подходящим вариантом: он сразу предоставлял нужный функционал и справлялся с высокими нагрузками. Кстати, информацию о Centrifugo команда узнала на одной из конференций: там цифровые гиганты рассказывали об обороте огромного трафика именно через этот real-time-сервер. Выбор был сделан. Мы остановились на версии Centrifugo 5.0 — на момент внедрения это было оптимальное сочетание стабильности и функциональных возможностей. Дальше началось самое интересное — процесс перехода, который занял у нас больше года.
Стратегия перехода: бесшовно и без сбоев
После принятия решения о миграции стало понятно, что откладывать процесс нельзя: к следующему праздничному сезону необходимо было подготовить систему к росту нагрузки. Поэтому в нашем распоряжении был примерно год, чтобы закрыть задачу. Мы начали переход без предварительной подготовки и, как водится, надеялись успеть все закончить за несколько дней, но это оказалось возможным только в мечтах.
В Centrifugo мы решили использовать приватные каналы для реал-тайм оповещений о событиях. Это дает нам уверенность в том, что мы всегда отправляем обновления чата конкретному пользователю. Реконнект или пинг к веб-сокету не нужен, так как он уже реализован внутри Centrifugo. Подписка на приватные каналы Centrifugo осуществляется непосредственно клиентами — мобильными приложениями — с использованием JWT, который сгенерирован на стороне сервера. А в JWT уже зашито название приватного канала, на который может подписаться клиент.

В теории план перехода выглядел элементарно: поднять свой микросервис, настроить рассылку для Centrifugo и выкачать всю базу из Firebase. Чтобы не уронить прод и сделать переход незаметным для пользователей, мы разработали детальный план.
Шаг первый: взяли весь трафик, связанный с чатами (сообщения, статусы, картинки), и завели его в единую точку входа. Все это должно было проходить не напрямую, как раньше, а через общий API. Как раз к тому моменту начал подрастать наш API-монолит, который сейчас является входной точкой для всех приложений компании: два клиентских приложения на iOS и Android и два приложения селлеров. Это действие позволило не только упростить интеграцию нового чата, но и усилить контроль над потоками данных.
Шаг второй: взялись за клиентские приложения. Архитектура на iOS и Android исторически отличалась, и это мешало внедрять общие решения. Несколько месяцев мы приводили все к единому стандарту и общей логике.
Параллельно с этим пилили новый микросервис для обработки сообщений. Важный момент: чтобы не рисковать, мы создали отдельные тестовые среды на реальных данных. Часть сервисов запускали в режиме «только для чтения» — это позволило точно оценить нагрузку и подготовиться к реальным нагрузкам.
Чаты мы разворачивали в трех независимых территориальных кластерах, каждый из которых сейчас обслуживается собственным инстансом Centrifugo, чтобы снизить задержки и повысить отказоустойчивость. Все инстансы поднимаются в Kubernetes, масштабируются через балансировщик нагрузки — это позволяет масштабировать поды для API-чатов и очередей.

Такой подход дал нам гибкость: мы могли одновременно писать данные и в старый Firebase, и в новый микросервис. Старые версии приложений продолжали работать, а данные синхронизировались. Каждую настройку мы обернули в «рубильник», чтобы в любой момент контролировать, куда идут данные. Для мобильных приложений также внедряли отдельные настройки, информирующие, какой механизм используется для работы с чатом.

Следующий квест: выкачать всю базу из Firebase. Это заняло недели две, потому что сервис отдавал информацию маленькими порциями и периодически падал. Процесс копирования пришлось повторять около 3 раз, чтобы убедиться, что мы ничего не потеряли. Вся база сообщений сейчас у нас хранится в MySQL.

Финал: выпиливание Firebase из нашей системы. Это было непросто: за годы у нас накопилось огромное количество кода, завязанного на Firebase, — фоновые команды, скрипты архивации и прочее легаси, которое помогало снизить нагрузку на облако. Раньше перед каждым пиком мы вручную чистили базу. Теперь в этом просто нет необходимости.
Через месяц после старта работы на новом решении мы полностью отключили Firebase. Наше первое боевое крещение — День матери — прошло идеально. Система спокойно выдержала более 90 тысяч одновременных соединений. А главное — мы знаем, что Centrifugo выдержал бы и в 10 раз больше.

Грабли переезда: с чем пришлось столкнуться на практике
Казалось бы, план переезда был простой, понятный и надежный, как швейцарские часы. Но, как водится, мы наступили на грабли там, где не ждали. Во многом это связано с тем, что вся работа проходила сумбурно, без предварительного технического анализа. Мы не знали, какие скелеты спрятаны в нашем шкафу, поэтому они вываливались уже в процессе переезда.
Главной проблемой стали мобильные приложения. Им больше 7 лет, писали их другие разработчики, и что там творилось под капотом, никто до конца не понимал. Будем честны, история знакома многим: уровень грамотности кода растет вместе с разработчиками. Привести две разные логики приложений к общему знаменателю, чтобы просто начать что-то менять, — вот на это и ушла львиная доля времени.
Вдобавок ко всему наш переезд совпал с глобальной перестройкой всей архитектуры Флаувау. Раньше система была монолитным приложением на старой версии Yii. Так что чаты мы переносили, одновременно распиливая наш старый монолит на микросервисы. Но это уже совсем другая история…
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.