Требования: личные и групповые чаты (до тысяч участников), доставка онлайн за доли секунды, история, статусы «доставлено/прочитано», работа с нескольких устройств, офлайн и пуш-уведомления. Оценка: 50 млн активных в день, 40 сообщений на пользователя — 2 млрд в день (~25 000/с, пик ×3), 10 млн одновременных соединений.
Соединения. Клиенты держат WebSocket (или MQTT) со шлюзами — тонкими сервисами на event loop, по сотне тысяч соединений на узел. Шлюз регистрирует в Redis «пользователь/устройство → шлюз». Балансировщик L4, переподключение с отсрочкой и джиттером при выкатке шлюзов.
Отправка:
- клиент шлёт сообщение с client_msg_id (UUID) — для идемпотентности повторов;
- сервис сообщений проверяет права, назначает номер в чате (монотонный счётчик на чат — в шарде чата) и пишет в хранилище;
- подтверждение отправителю с номером;
- событие «новое сообщение» уходит в шину, сервис доставки находит шлюзы участников и отправляет; офлайн-устройствам — пуш (FCM/APNs).
Хранилище истории: запросы «последние N сообщений чата» и «после номера X» → wide-column (Cassandra, ScyllaDB) с ключом партиции chat_id (и бакетом по времени для очень больших чатов) и сортировкой по номеру; или шардированный PostgreSQL по chat_id. Медиа — в объектном хранилище, в сообщении — ссылка.
Порядок. Время клиента ненадёжно, время серверов расходится. Порядок внутри чата задаёт номер, выданный одним местом (лидер шарда чата). Между чатами порядок не нужен. Клиент показывает по номеру и дозапрашивает пропуски.
Доставка и синхронизация. Каждое устройство помнит последний полученный номер по каждому чату (или глобальный курсор «ящика» пользователя). После переподключения — «отдай всё новее курсора». Доставка онлайн — at-least-once с дедупликацией на клиенте по номеру.
Групповые чаты: до сотен участников — рассылка по участникам; для тысяч и каналов — не раскладывать в «ящики» каждого, а читать из чата (как fan-out on read в ленте), уведомлять только онлайн.
Статусы «доставлено/прочитано» — курсоры на участника и чат (последний прочитанный номер), обновляются пачками, не на каждое сообщение.
Ещё: сквозное шифрование меняет архитектуру (сервер не видит содержимое — поиск и модерация на клиенте), поиск по истории — отдельный индекс, лимиты на спам, хранение по закону.