Демонстрация дизайна системы

Аудитория

Эта статья является следующей в моей серии статей о том, как я буду разрабатывать популярные приложения. Рекомендуется (хотя и не совсем обязательно) прочитать предыдущие посты, которые я услужливо собрал в список здесь. Мы ожидаем базового знакомства с принципами архитектуры и AWS, но надеемся, что этот пост будет доступен большинству инженеров.

Аргумент

Сначала давайте посмотрим на нашу постановку задачи.

Система для проектирования

Мы воссоздаем популярную социальную сеть Twitter. Предпосылка (для тех из вас, кто не знаком) заключается в том, что пользователи могут отправлять короткие текстовые твиты максимальной длины (скажем, 140 символов). Они также могут добавлять хэштеги, которые представляют собой способ группировки твитов схожей темы.

Люди подписываются друг на друга, чтобы увидеть контент, который они публикуют. Список контента, который видит пользователь, — это его «домашняя временная шкала», а список опубликованного контента — его «пользовательская временная шкала». Для целей упражнения у нас есть следующие требования.

  1. Разрешить пользователям публиковать твиты
  2. Разрешить пользователям получать свои домашние и пользовательские временные шкалы
  3. Обеспечить поиск твитов по хэштегу
  4. Отобразите трендовые хэштеги для пользователя.

Будем считать, что функция следования за нас позаботилась. Если интересно, предлагаемое решение можно найти в моей статье о дизайне в Instagram здесь.

Подход

У нас стандартный подход к проектированию системы, который более подробно описан в статье здесь. Однако шаги кратко изложены ниже:

  1. Уточнение требований: убедитесь, что у нас есть вся информация, прежде чем начать. Это может включать в себя ожидаемое количество запросов или пользователей.
  2. Задняя часть оценки конверта:Выполнение некоторых быстрых расчетов для оценки необходимой производительности системы. Например, сколько памяти или пропускной способности нам нужно?
  3. Дизайн интерфейса системы: как наша система будет выглядеть снаружи, как люди будут с ней взаимодействовать? Как правило, это контракт API.
  4. Дизайн модели данных. Как будут выглядеть наши данные при их хранении. На этом этапе мы могли бы подумать о реляционных и нереляционных моделях.
  5. Логический дизайн:собираем все вместе в грубую систему! В этот момент я думаю на уровне «как бы я объяснил свою идею тому, кто ничего не знает о технологиях?»
  6. Физический дизайн. Теперь мы начинаем думать о серверах, языках программирования и деталях реализации. Мы можем наложить их поверх логического дизайна.
  7. Выявление и устранение узких мест: На этом этапе у нас будет работающая система! Теперь дорабатываем дизайн.

С учетом сказанного, давайте застрянем!

Уточнение требований

Первое, что меня интересует, это сколько пользователей мы ожидаем и как часто они читают/пишут/ищут твиты. Допустим, у нас 500 миллионов пользователей. Каждый пользователь читает 100 твитов в день и публикует 1. Обычно они ищут и просматривают популярные хэштеги один раз в день.

Задняя часть оценки конверта

Если у нас есть 500 миллионов пользователей, читающих 100 твитов по 140 символов, это эквивалентно 500,000,000 * 100 * 140 bytes = 7 terabytes чтениям в день, что эквивалентно 81MB/s или почти 600,000 запросам в секунду!

Для наших возможностей записи/поиска/просмотра у нас всего 70GB трафика в день и 810KB/s/6000RP/s. Мы уже видим, что у нас есть тяжелое для чтения приложение…

Дизайн системного интерфейса

Уточнив требования, теперь пришло время разработать наши интерфейсы. Нам нужны конечные точки для следующих

Чтобы опубликовать твит, мы могли бы сделать POST запрос к конечной точке user/{id}/tweets для создания нового объекта твита. Применяются обычные коды ответов 201, 4XX, 5XX. Предположим, что запрос поступает с файлом cookie, который мы можем проверить и использовать, чтобы проверить, вошли ли они в систему как пользователь, соответствующий параметру пути.

Получение твитов пользователя может быть выполнено с помощью запроса GET к /users/{id}/tweets с кодами ответов 200, 4XX, 5XX. Получить домашнюю временную шкалу пользователя можно через /tweets/homes/users/{id}.

Поиск будет использовать конечную точку /tweets?hashtag=<enteredValue>, и мы можем возвращать хэштеги, используя /hashtags/trending. Последняя конечная точка подлежит обсуждению (если не все из них, структура URL-адресов — это непрекращающаяся война). Мы могли бы сделать что-то более гибкое с разбиением на страницы и сортировкой, но это кажется неуместным для пользователя.

Дизайн модели данных

Теперь мы знаем, как получить доступ к нашим данным, как это будет выглядеть! В основном мы ориентируемся на твиты и хэштеги. Наши твиты будут выглядеть

{
  "user_name" : "<Name of the user>",
  "text": "<Text of the tweet>"
  "hash_tags" : "<Array of hashtags>"
}

Наш объект хэштега может быть просто

{
  "text": "<Text of the hashtag>"
}

Использование объекта хэштега вместо простой строки обеспечивает некоторую гибкость, если мы хотим добавить другую информацию (например, количество посещений) позднее.

Мы также хотим подумать о том, какое хранилище данных мы хотели бы использовать. Из наших расчетов обратной стороны конверта мы увидели, что прочитали тяжелые требования. Это говорит о том, что мы будем использовать нереляционные базы данных с ключом-значением для оптимизации чтения. Столбцы могут быть похожими на:

Пользователи

  • id VARCHAR PRIMARY KEY
  • name VARCHAR

Твиты

  • id VARCHAR PRIMARY KEY
  • text VARCHAR
  • user_id VARCHAR FOREIGN KEY (user_id) REFERENCES user id
  • created_at TIMESTAMP

Ассоциация хэштегов

  • id VARCHAR PRIMARY KEY
  • tweet_id VARCHAR FOREIGN KEY (tweet_id) REFERENCES tweet id
  • hashtag_id VARCHAR FOREIGN KEY (hashtag_id) REFERENCES hashtag id

Хештеги

  • id VARCHAR PRIMARY KEY
  • text VARCHAR

Обратите внимание, что мы пропустили таблицу ассоциаций для связи пользователей друг с другом для отслеживания, однако это выходит за рамки упражнения.

Логический дизайн

Теперь мы переходим к логическому дизайну. Есть несколько требований, которые достаточно разделены, поэтому мы можем сосредоточиться на них по отдельности, а затем совместить их вместе. Разделы, на которых мы сосредоточимся, — это чтение и письмо, поиск и трендовые хэштеги.

Первоначально мы будем использовать шаблон стиля CQRS для чтения и записи.

Идея, стоящая за этим, состоит в том, чтобы разделить наши две проблемы. Мы размещаем тонкий шлюз перед всеми запросами, направляя их в соответствующий сервис. Когда кто-то пишет твит, он записывается в базу данных записи, которая, в свою очередь, синхронизирует отдельные, полностью сформированные представления для пользователя и домашней ленты в базе данных чтения. Обратите внимание, что это означает, что мы занимаем больше места, но сокращаем время загрузки.

Чтобы прочитать твиты пользователя или его домашнюю ленту, мы можем затем запросить базу данных чтения через службу чтения, которая сможет вернуть оба списка полностью сформированными и готовыми к работе!

Поиск опять стал другим. Мы можем использовать поиск по инвертированному индексу, который будет хранить хэштеги по идентификаторам содержащих их твитов. Затем мы можем получить твиты из хранилища данных. Обратите внимание, что это хранилище данных может быть нашей базой данных для записи или для уменьшения нагрузки это может быть другое хранилище данных, в которое мы записываем специально для поиска.

Наконец, нам нужен метод поиска лучших хэштегов. Это можно сделать с помощью набора потоковых сервисов, которые могут агрегировать количество хэштегов по мере поступления твитов.

Чтобы агрегировать информацию о хэштегах, мы принимаем твиты в виде потока, а затем передаем их анализатору, который отделяет хэштег и данные о местоположении. Хотя это не является обязательным требованием, в этом есть смысл. Представьте, что в Португалии идет популярная футбольная игра, скорее всего, пользователям в Мумбаи это может быть неинтересно!

Счетчики местоположений и хэштегов работают в окне и агрегируют данные по мере их поступления, возвращая количество тегов и местоположений в этом окне (например, окно может длиться пять минут).

Затем эти подсчеты передаются другому набору служб, отвечающих за сохранение данных в хранилище данных. Взаимодействие с хранилищем данных будет значительно сокращено, поскольку мы будем получать только пакеты данных для каждого окна.

Давайте объединим все это в один окончательный логический проект!

Физический дизайн

Теперь мы можем преобразовать это в физическую диаграмму.

Вышеизложенное представляет, как мы могли бы физически реализовать нашу систему. Мы можем использовать AWS API Gateway в качестве службы шлюза, которая затем перенаправляет запросы на запись в поток Kinesis, а запросы на чтение/поиск — в Lambdas. Нам нужно быть осторожными, чтобы не исчерпать наш лямбда-пул (особенно с 6000RPS), но теоретически это нормально.

После этого поток записи Kinesis будет считываться тремя разными приложениями.

  1. Служба потоковой передачи Spark на AWS EMR, которая будет отвечать за анализ твитов на информацию о местоположении и хэштегах.
  2. Kinesis Firehose, который будет передавать данные в хранилище данных Amazon Redshift.
  3. Kinesis Firehose, который будет отправлять данные в AWS OpenSearch, управляя нашим поиском по инвертированному индексу.

Компонент поиска нашей проблемы полностью обрабатывается службами Spark Streaming в EMR. Между службами могут потребоваться дополнительные потоки, но для краткости они опущены.

Наши базы данных чтения реализованы с использованием AWS DynamoDb, высоконадежной и производительной нереляционной базы данных Amazon.

Заключительная часть нашей системы для обсуждения записи в Redshift и DynamoDb. На диаграмме видно, что мы передаем все твиты непосредственно в базу данных для чтения, объединяя разделение хранилищ данных для чтения и записи в CQRS. В нашем случае это нормально, поскольку мы продолжаем разделять службы чтения и записи.

Выявление и устранение узких мест

Основное узкое место, на котором мы сосредоточимся, — это когда очень популярный пользователь публикует твит. Если знаменитость со 100 миллионами подписчиков отправит твит, нам нужно будет обновить миллионы просмотров в базе данных чтения, что займет много времени!

Чтобы оптимизировать это, мы могли бы ввести новую реляционную базу данных знаменитостей с их твитами. Служба чтения будет объединять популярные твиты и просмотры пользователей, чтобы создать более производительное решение.

Вывод

В заключение мы рассмотрели, как мы будем разрабатывать и реализовывать крупномасштабную текстовую платформу для социальных сетей. Надеюсь, вам понравилось!

Н.Б. Я никоим образом не являюсь экспертом в крупномасштабной обработке данных! Это мое лучшее предположение, и его следует рассматривать как таковое. Дайте мне знать, если что-то кажется неправильным!