В качестве сервиса для проектирования была выбрана стриминговая площадка Apple Music, предоставляющая доступ к композициям из iTunes. 1, 2.
Регион | Аудитория (млн) |
---|---|
Северная Америка | 33 |
Индия | 10 |
Великобритания | 5 |
Канада | 4 |
- Аутентификация и регистрация
- Поиск музыки
- Стриминг музыки
- Личная библиотека и плейлисты
- Рекомендации
- Интеграция с iCloud Music Library
- ALAC/AAC
- Интеграция текстов треков с Genius
Метрика | Значение метрики |
---|---|
Месячная аудитория | 93 млн |
Дневная аудитория | 46.2 млн3 |
Хранимые данные | Размер на одного пользователя |
---|---|
Аватар | 2 МБ |
Мета информация | 1 МБ |
Действие пользователя | Количество / день |
---|---|
Регистрация | 16.71 тыс |
Авторизация | 1.54 млн |
Прослушивание музыки | 140 минут |
Создание плейлистов | 185.7 тыс |
Добавление в плейлист | 4485,37 тыс |
Открытие страницы исполнителя | 15.4 млн |
-
По статистике 1 в 2021 году Apple music насчитывал 78.6 млн пользователей, а в 2022 году уже 84.7 млн. Вычитаем эти числа и получаем 84.7 - 78.6 = 6.1 мл прироста пользователей за год. Делим это число на количество дней и получаем среднее количество регистраций в день
6.1/365 = 0.01671 млн = 16 тыс в день
Среднее количество авторизаций в день можно примерно вычислить зная DAU, которое равно 46.2 млн пользователей в день. Учитывая, что auth token живет месяц, делим это число на 30 и получаем примерное кол-во авториъзаций в день.
46.2/30 = 1.54 млн в день
-
Так как не удалось найти информацию о том, сколько треков в день слушает пользователь Apple Music были использованы данные Spotify 4. На данной платформе пользователи в среднем в день слушают 40 треков, средняя длительность трека составляет 3.5 минут.
40 * 3.5 = 140 минут в день
-
Аналогично, используя статистику Spotify, можно узнать сколько в среднем плейлистов пользователи создают за день. Это количество равняется 1.3 млн в день 5. Учитывая что аудитория Spotify превосходит аудиторию Apple Music в 7 раз, то для того чтобы узнать сколько в среднем плейлистов за день создают пользователи Apple Music можно разделить 1.3 млн на 7.
1.3 / 7 = 0.1857 млн = 185.7 тыс в день
- Для расчета обьема хранилища, выделяемого под пользователей, умножим средний размер хранилища на одного пользователя на кол-во пользователей
В Apple Music треки хранятся в стандартном качестве AAC с битрейтом до 256 кбит/с, средний вес такого трека составляет 7 МБ. Также есть возможность слушать музыку в Lossless качестве 16-бит/44.1 кГц (CD качество) и средний вес такого трека составляет 40 МБ. Соответственно примем, что треки хранятся в двух видах качества и занимают в среднем 47 МБ. Исходя из того что библиотека Apple Music насчитывает около 100 млн треков 6, можно рассчитать суммарный обьем хранилища, выделяемого под музыку.
3 * 93 = 279 ТБ
47 * 100 = 4700 ТБ
Хранилище | Размер |
---|---|
Пользователи | 279 ТБ |
Музыка | 4700 ТБ |
Действие пользователя | RPD | RPS | Пиковое потребление | Суммарный суточный,Гбайт/сутки |
---|---|---|---|---|
Регистрация | 16.71 тыс | 0.1932 | 7.293 Мбит/c | 48.90 |
Авторизация | 1.54 млн | 18.18 | 0.457 Гбит/с | 4596.63 |
Создание плейлистов | 185.7 тыс | 2.1490 | 9.81 Мбит/c | 65.86 |
Добавление в плейлист | 4485,37 тыс | 51.915 | 196.2 Мбит/c | 44.25 |
Открытие страницы исполнителя | 15.4 млн | 171.1111 | 0,42045 Гбит/с | 4229 |
- Мейден, Северная Каролина, США. Один из крупнейших ЦОДов площадью в 500 000 кв. футов. Частично работает на солнечной энергии.
- Уоки, Айова, США. Строительство этого ЦОДа началось в 2022 году и на данный момент его площадь составляет 398 473 квадратных фута. В общей сложности Apple планирует построить в этом месте 6 центров обработки данных площадью около 2 миллионов квадратных футов.
- Меса, Аризона, США. Этот дата-центр площадью 1,3 миллиона квадратных футов служит ключевым логистическим и операционным центром, поддерживающим все центры обработки данных Apple по всему миру. Этот ЦОД был оборудован на основе фабрики одного из бывших поставщиков Apple, обьявившим о банкротстве. Открылся в 2017 году.
- Прайнвиль, Орегон, США. ЦОД площадью 3 млн квадратных футов, чье строительство началось в 2014 году, а в 2021 началось значительное расширение.
- Рино, Невада, США. ЦОД площадью 1 млн квадратных футов.
- Выборг, Дания. Apple открыла свой дата-центр в Выборге, Дания, в 2020 году, построив помещение площадью 484 376 квадратных футов. В течение 2022 и 2023 Apple активно расширяла этот ЦОД, в частности подключила свой дата-центр в Выборге к системе централизованного теплоснабжения, чтобы использовать избыточную тепловую энергию. В конечном счете Apple планирует расширить этот ЦОД до 1.8 млн квадратных футов.
- Ньюарк, Нью-Джерси, США. Приобретен Apple в 2006 году, с площадью 107 тыс. кв. футов.
- Тоберро, Ирландия. Имеет площадь 324 тыс. кв. футов. Выкуплен Apple в 2014 году, планируется расширение до 1.8 млн. кв. футов.
- Гуйян и Уланкаб, Китай. Согласно китайскому законодательству, Apple обязана размещать данные своих китайских пользователей в местных центрах обработки данных. Таким образом, у Apple есть два действующих центра обработки данных в Китае, которые работают в партнерстве с Guizhou-Cloud Big Data Industry Development Co., Ltd. (GCBD), поставщик услуг в материковом Китае, контролируемый государством. Географически Apple располагает одним дата-центром в городе Гуйян, столице провинции Гуйчжоу, и еще одним в городе Уланкаб, входящем в состав региона Внутренняя Монголия.
Наиболее оптимальным решением для DNS балансировки будет Geo-based DNS. Все дело в том что DNS достаточно хорошо работает на уровне стран - континентов, т.к. айпи адреса хорошо локализованы по континентам и странам. Сервер выдаст адрес ближайшего к пользователю ДЦ.
Однако при балансировки внутри страны могут быть проблемы, т.к. внутри страны айпи адреса географически сложно отличить. Поэтому между ДЦ механизм регулировки трафика будет другой.
Даже когда мы уже получили айпи адрес из DNS, мы еще можем влиять куда он приземлится физически. Для этого используется технология BGP Anycast.
На уровне ДЦ устанавливаем маршрутизатор, распределяющий трафик с помощью BGP на L3 балансировщик.
Используем Linux Virtual Server via Direct Routing. При таким варианте реализации маршрутизатор отправляет запросы на виртуальные ip адреса, при этом на сетевом уровне айпи пакет остается неизменным, а в ethernet фрейме меняется мак адрес назначения на основе загрузки сервера, отправляя их на определенные L7 балансировщики. Ответы отправляются напрямую клиентам, минуя L3 балансировщик. Стоит учесть что такой вариант предполагает нахождение внутри одной физической сети.
Для управления балансировщиком и отслеживания состояния L7 балансировщика используем keepalived, который следит за состоянием кластера и посылает специальные запросы на хосты, проводит внутренний health check.
Используем HTTP Reverse Proxy в качестве которого выступает Nginx. Он будет распределять запросы от клиентов по нескольким кластерам и решать проблему медленных юзеров (когда воркер сразу отдает ответ Nginx а не ждет пока клиент его получит).
Используем Kubernetes для оркестрации контейнеров, он занимается автоматическим запуском подов, балансирует нагрузку между ними а также выполняет масштабирование с помощью auto-scaling.
Таблица | Описание | Требование к консистентности | Размер |
---|---|---|---|
User | Хранит информацию о пользователях сервиса. Каждый пользователь имеет уникальный идентификатор id, email и username. | id - PK, email - UNIQUE, username - UNIQUE | 4 + 20 + 30 + 8 + 50 + 20 = 132 байта |
Session | Хранит информацию о текущих активных сеансах пользователей. Каждая сессия имеет уникальный идентификатор session_id а также хранит идентификатор пользователя user_id и время окончания действия сеанса expiration | session_id - PK | 8 + 8 + 8 = 24 байта |
Playlist | Хранит информацию о плейлистах пользователей. Каждый плейлист имеет уникальный id. Также хранится информация о его названии, его описание, картинка и внешний ключ на пользователя которому принадлежит плейлист. | playlist_id - PK | 8 + 8 + 20 + 50 + 30 = 116 байт |
Song | Хранит информацию о треках. Каждый трек имеет уникальный id. Также хранится информация о его названии, ссылка на файл в s3, количество прослушиваний и длительность трека. Также имеется внешний ключ на альбом. | id - PK | 8 + 20 + 50 + 4 + 2 + 8 = 92 байтa |
Playlist_Song | Хранит информацию о треках в плейлистах. | playlist_id, song_id - PK | 8 + 8 = 16 байт |
LikedSongs | Хранит информацию о треках, которые лайкнул пользователь. | song_id, user_id - PK | 8 + 8 = 16 байт |
Genre | Хранит информацию о жанрах. Каждый жанр имеет уникальный id и имя. | id - PK, name - UNIQUE | 4 + 20 = 24 байтa |
Genre_Song | Хранит информацию о жанрах песен. | genre_id, song_id - PK | 4 + 8 = 12 байт |
Album | Хранит информацию об альбомах. Каждый альбом имеет уникальный id, а также хранится информация о его названии, картинке, дате публикации и внешний ключ на автора. | id - PK | 8 + 30 + 50 + 8 + 4 = 100 байт |
Artist | Хранит информацию об исполнителях. Каждый исполнитель имеет уникальный id. Также хранится информация о его имени, количестве прослушиваний в месяц, описание, дата рождения и жанр. | id - PK | 4 + 20 + 2 + 256 + 2 + 4 = 288 байт |
Таблица | СУБД | Индексы |
---|---|---|
Session | Redis | session_id - поиск сессии по идентификатору |
User | MongoDB | id - при поиске пользователя через плейлист,username - при поиске пользователем других пользователей, email - при входе |
Song | MongoDB + S3 | id , name - при поиске пользователем трека по названию, album_id - индекс по альбому |
Playlist | MongoDB | name - при поиске пользователем по названию, user_id - при просмотре плейлистов другого пользователя |
Playlist_Song | MongoDB | playlist_id - поиск трека в конкретном плейлисте |
LikedSongs | MongoDB | user_id - при просмотре добавленных треков |
Genre | MongoDB | name - при поиске жанра по названию |
Genre_Song | MongoDB | genre_id - при поиске песен по жанру |
Album | MongoDB | id, name - при поиске альбома по названию, artist_id - при просмотре альбомов исполнителя |
Artist | MongoDB | id - при поиске исполнителя альбома, name - при поиске исполнителя по имени |
- User - горизонтальное шардирование по id
- Playlist - горизонтальное шардирование по user_id
- Album - горизонтальное шардирование по id
- Song - горизонтальное шардирование по album_id
- Artist - горизонтальное шардирование по id
- MongoDB - Managed Service будет обеспечивать автоматическое резервное копирование
- Redis - Managed Service for Redis обеспечивает автоматическое и ручное резервное копирование баз данных.
Для аналитики данных наиболее удачным выбором будет Clickhouse из за своей большой скорости и возможности очень быстро читать запросы на больших обьемах данных.
Обработкой и передачей данных в clickhouse будет заниматься Kafka.
Классическая реализация этого алгоритма основана на на принципе n ближайших соседей. Ищем для каждого пользователя n наиболее похожих на него пользователей и дополняем информацию о пользователе данными о его соседях. В матрице выше желтым цветом обозначен пользователь, для которого необходимо найти оценки по новому контенту (знаки вопроса). Синим цветом выделены три ближайших к нему соседа. У классической реализации данного алгоритма есть явный минус - он плохо применим на практике из за квадратичной сложности. Отчасти эта проблема решается мощностью железа но также можно внести некоторые корректировки в алгоритм:
- не пересчитывать матрицу расстояний полностью а обновлять ее инкрементально
- сделать выбор в пользу итеративных и приближенных алгоритмов
Явный фидбек от пользователей в сервисе прослушивания музыки недоступен, поэтому приходится ориентироваться на неявный (кол-во прослушиваний, лайков). Допустим человек не слушал песню. Значит ли это что она ему не нравится? Или просто среди миллионов треков ему еще не попался эта конкретная песня? А если человек прослушал только часть песни, значит ли это что она ему не понравилась? Может самую запоминающуюся часть он все таки прослушал? Таким образом, рассуждения переходят из дихотомии понравилось/не понравилось в пространство степени уверенности: «мы не уверены, что полсекунды прослушивания означают, что ему понравилось, но гораздо сильнее уверены, что три минуты прослушивания означают, что ему понравилось» Стандартный ALS метод преобразуется таким образом в новую функцию для оптимизации, с которой можно работать в рамках описанного выше алгоритма.
Технология | Где применяется | Мотивация |
---|---|---|
Golang | Бэкенд, бизнес логика приложения | Наилучший вариант для высоконагруженных распределенных систем благодаря своей эффективной параллельной обработке |
MongoDB | Хранение данных, OLTP | Быстро развивающаяся, надежная и хорошо задокументированная БД |
Redis | Хранение данных сессии | высокопроизводительная in-memory база данных |
k8s | Оркестрация контейнеров | Используем kubernetes для балансировки и автомасштабирования бекенда |
S3 | Обьектное хранилище, хранение файлов музыки | Надежность, масштабируемость |
Clickhouse | Хранение данных для аналитики, OLAP | Огромная скорость, хранит данные очень компактно в виде одной большой таблицы |
Kafka | стриминговая БД, сбор большого потока событий-данных | Предоставляет единообразную платформу с высокой пропускной способностью и низкой задержкой для обработки данных в режиме реального времени. |
-
Auth: Сервис авторизации пользователей. Рабтает в связке с Redis
-
User: Сервис регистрации пользователей и взаимодействия их со своим профилем. Работает в связке с MongoDB и хранит пользовательский кеш в Redis.
-
Playlist: Сервис, отвечающий за работу с плейлистами. Работает в связке с MongoDB.
-
Artist: Сервис, отвечающий за профили исполнителей. Работает в связке с MongoDB.
-
Recommendations: Сервис, который генерирует рекомендации для пользователей. Собирает данные о пользователе из других сервисов через Кафку. Работает в связке с MongoDB.
-
File Service: Сервис, работающий с файлами. Занимается их процессингом, удалением и очисткой от мусора. Работает в связке с MongoDB и S3.
-
Streaming Service: Сервис, отвечающий за стриминг музыки пользователю. Работате в связке с S3.
-
Analytics: Сервис, собирающий данные для аналитики через Кафку. Работает в связке с Clickhouse.
-
Logs: Собирает логи. Работает в связке с MongoDB.
-
Репликация в MongoDB осуществляется через наборы реплик (replica sets). Набор реплик состоит из нескольких экземпляров MongoDB, которые хранят одинаковые наборы данных. Один из узлов выступает в роли мастера, принимающего записи, в то время как остальные узлы являются слейвами, которые получают обновления от мастера. В случае сбоя мастера, один из слейвов может быть автоматически переведен в режим мастера.
-
MongoDB поддерживает автоматическое переключение (failover) в рамках наборов реплик. Если основной узел выходит из строя, система автоматически выбирает нового мастера из доступных слейвов.
-
Redis Sentinel — это система мониторинга и управления, которая обеспечивает автоматическое переключение на резервный узел в случае сбоя мастера. Sentinel следит за состоянием узлов и может автоматически перенастроить систему, чтобы обеспечить непрерывность работы.
-
Redis поддерживает различные механизмы сохранения данных, такие как RDB (Redis Database Backup) и AOF (Append Only File). RDB создает снимки базы данных через заданные интервалы времени, а AOF не изменяет уже записанные данные, а лишь добавляет новые в конец.
Используем механизмы keepalived и health checks.
- Self-Healing (Самовосстановление) Kubernetes автоматически обнаруживает и устраняет проблемы с объектами в кластере: Проверка состояния (Health Checks): Liveness Probe — проверяет, работает ли приложение. Если нет, Kubernetes перезапускает контейнер. Readiness Probe — проверяет, готово ли приложение принимать трафик. Если оно не готово, Kubernetes исключает его из балансировки. Автоматический перезапуск Pod-ов: Если Pod выходит из строя, контроллеры (например, Deployment) автоматически перезапускают его.
- Репликация и масштабирование ReplicaSets: Кластеры поддерживают заданное число экземпляров (реплик) Pod-ов. Если какой-то Pod выходит из строя, ReplicaSet автоматически создаёт новый. Автоматическое масштабирование (Horizontal Pod Autoscaler): Kubernetes может автоматически увеличивать или уменьшать количество Pod-ов в зависимости от нагрузки (CPU, RAM и других метрик). Cluster Autoscaler: Автоматическое добавление или удаление узлов кластера в зависимости от потребностей.
- Балансировка нагрузки Service Discovery и Load Balancing: Kubernetes автоматически распределяет трафик между Pod-ами через абстракцию Service. ClusterIP: Для внутренней балансировки трафика. NodePort и LoadBalancer: Для внешнего доступа. Ingress: Предоставляет более сложные правила маршрутизации и балансировки.
Будет обрабатываться сигнал остановки сервиса. Приём новых запросов будет приостановлен. Только после того как выполнятся запросы, которые уже успели дойти, только после этого отключаем сервис.
- При получении сигнала SIGTERM от Kubernetes, наше приложение должно начать процесс Graceful Shutdown. Это включает в себя завершение обработки текущих запросов, закрытие соединений с БД и другие задачи по очистке.
- Kubernetes предоставляет период завершения, в течение которого приложение может завершить свою работу перед тем, как будет принудительно остановлено с помощью SIGKILL. Этот период можно настроить.
- Kubernetes предоставляет хуки жизненного цикла, которые позволяют запускать определенные команды на различных этапах жизненного цикла пода. Мы можем использовать хук preStop для запуска скрипта или команды, которая начнет процесс Graceful Shutdown.
- При начале процесса Graceful Shutdown, под должен быть удален из балансировщика нагрузки, чтобы новые запросы не были направлены на него. Kubernetes автоматически удаляет под из Endpoint'ов службы при получении сигнала SIGTERM.
Основное назначение Apple music - всегда иметь возможность к прослушиванию музыки.
Следующие сервисы должны работать для обеспечения надёжности продукта:
- Auth
- User
- Playlists
- Artist
- Streaming
Допускается потеря части функционала:
- Recommendations: в случае отказа сервиса использовать старые рекомендации
- Analytics
Для того чтобы быстро отслеживать запросы будем использовать паттерн RequestId. Сами логи будут храниться в MongoDB в виде JSON.
Service | RPS | CPU | RAM | NET |
---|---|---|---|---|
Auth | 18.3732 | 2 | 3 ГБ | 0.464 ГБ/c |
User | 1.2 | 1 | 1 ГБ | 0.16 ГБ/с |
Playlist | 54.063 | 2 | 4 ГБ | 0.206 ГБ/с |
Artist | 171.1 | 4 | 6 ГБ | 0.42045 ГБ/с |
Recommendations | 534.72 | 10 | 16 ГБ | 0.14 ГБ/с |
Streaming | 21 388.9 | 250 | 300 ГБ | 55.13 ГБ/с |
Transcoding | 0.57 | 2 | 2 ГБ | 0.1368 МБ/с |
Service | CPU/r | CPU/l | RAM/r | RAM/l | Cnt |
---|---|---|---|---|---|
Auth | 1 | 1 | 1000 | 1500 | 3 |
User | 1 | 1 | 600 | 900 | 3 |
Playlist | 1 | 1 | 1100 | 1500 | 3 |
Artist | 1 | 2 | 1500 | 2000 | 3 |
Recommendation | 3 | 4 | 4700 | 5400 | 3 |
Streaming | 17 | 20 | 15 000 | 20 000 | 15 |
Transcoding | 1 | 1 | 500 | 700 | 3 |
Источники: