Как лучше всего реализовать поток социальной активности?

Мне интересно услышать ваше мнение о том, как лучше всего реализовать поток социальной активности (наиболее известный пример - Facebook). Проблемы / проблемы включают в себя:

  • Различные виды деятельности (отправка, комментирование..)
  • Различные типы объектов (пост, комментарий, фото..)
  • 1-n пользователей, участвующих в разных ролях ("Пользователь x ответил на комментарий пользователя y к записи пользователя Z")
  • Различные представления одного и того же элемента действия ("Вы прокомментировали.." против "Ваш друг прокомментировал" против "Пользователь x прокомментировал.." => 3 представления действия "Комментарий")

... и еще кое-что, особенно если вы берете его на высокий уровень сложности, как это делает Facebook, например, объединяя несколько элементов активности в один ("пользователи x, y и z прокомментировали эту фотографию")

Будем благодарны за любые мысли или указания на шаблоны, документы и т. Д. О наиболее гибких, эффективных и мощных подходах к реализации такой системы, модели данных и т. Д.

Хотя большинство проблем не зависит от платформы, скорее всего, я в конечном итоге внедряю такую ​​систему на Ruby on Rails

13 ответов

Я создал такую ​​систему, и я использовал этот подход:

Таблица базы данных со следующими столбцами: идентификатор, идентификатор пользователя, тип, данные, время.

  • userId - это пользователь, который сгенерировал активность
  • тип - тип действия (т.е. написал сообщение в блоге, добавил фотографию, прокомментировал фотографию пользователя)
  • data - это сериализованный объект с метаданными для действия, в который вы можете поместить все, что захотите

Это ограничивает поиск / поиск, который вы можете выполнять в каналах, пользователями, временем и типами активности, но в фиде типа Facebook это на самом деле не ограничивает. И с правильными индексами на столе поиск быстр.

С этим дизайном вам нужно будет решить, какие метаданные должен требовать каждый тип события. Например, активность канала для новой фотографии может выглядеть примерно так:

{id:1, userId:1, type:PHOTO, time:2008-10-15 12:00:00, data:{photoId:2089, photoName:A trip to the beach}}

Вы можете видеть, что, хотя название фотографии наверняка хранится в какой-то другой таблице, содержащей фотографии, и я мог бы извлечь имя оттуда, я дублирую имя в поле метаданных, потому что вы не хотите делать любые соединения в других таблицах базы данных, если вы хотите скорость. А чтобы отобразить, скажем, 200 разных событий от 50 разных пользователей, вам нужна скорость.

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

Это очень хорошая презентация, рассказывающая о том, как Etsy.com спроектировал свои потоки активности. Это лучший пример, который я нашел по этой теме, хотя он не является специфическим для рельсов.

http://www.slideshare.net/danmckinley/etsy-activity-feeds-architecture

Мы использовали наш подход с открытым исходным кодом: https://github.com/tschellenbach/Stream-Framework В настоящее время это самая большая библиотека с открытым исходным кодом, предназначенная для решения этой проблемы.

Та же команда, которая создала Stream Framework, также предлагает размещенный API, который решает эту сложность для вас. Взгляните на https://getstream.io/. Есть клиенты, доступные для Node, Python, Rails и PHP.

Кроме того, взгляните на этот пост с высокой масштабируемостью, где мы объясняем некоторые из проектных решений: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic-feeds.html

Этот учебник поможет вам настроить систему, такую ​​как фид Pinterest, с помощью Redis. Это довольно легко начать.

Чтобы узнать больше о дизайне каналов, я настоятельно рекомендую прочитать некоторые статьи, на которых мы основывали Feedly:

Хотя Stream Framework основан на Python, его будет не сложно использовать в приложении на Ruby. Вы можете просто запустить его как сервис и поставить перед ним небольшой http API. Мы рассматриваем возможность добавления API для доступа к Feedly с других языков. На данный момент вам придется сыграть свою собственную роль.

Самые большие проблемы с потоками событий - видимость и производительность; вам нужно ограничить отображаемые события только интересными для этого конкретного пользователя, и вам нужно сохранить количество времени, необходимое для сортировки и идентификации этих событий, управляемым. Я построил небольшую социальную сеть; Я обнаружил, что в небольших масштабах сохранение таблицы "событий" в базе данных работает, но это становится проблемой производительности при умеренной нагрузке.

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

Я полагаю, что это был оригинальный недостаток дизайна Twitter- я помню, как читал, что они использовали базу данных, чтобы включить и отфильтровать свои события. Это было все, что связано с архитектурой, и не имело ничего общего с Rails, который (к сожалению) породил мем "рубин не масштабируется". Недавно я видел презентацию, в которой разработчик использовал Amazon Simple Queue Service в качестве бэкэнда для обмена сообщениями для приложения, похожего на твиттер, с гораздо более широкими возможностями масштабирования - возможно, стоит рассмотреть SQS как часть вашей системы, если ваши нагрузки достаточно высоки.,

Если вы хотите использовать отдельное программное обеспечение, я предлагаю сервер Graphity, который точно решит проблему для потоков активности (построение поверх базы данных графика neo4j).

Алгоритмы были реализованы как отдельный REST-сервер, так что вы можете разместить свой собственный сервер для доставки потоков активности: http://www.rene-pickhardt.de/graphity-server-for-social-activity-streams-released-gplv3/

В статье и тесте я показал, что получение потоков новостей зависит только линейно от количества элементов, которые вы хотите получить без какой-либо избыточности, которую вы получили бы при денормализации данных:

http://www.rene-pickhardt.de/graphity-an-efficient-graph-model-for-retrieving-the-top-k-news-feeds-for-users-in-social-networks/

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

Я начал внедрять такую ​​систему вчера, вот где я должен...

Я создал класс StreamEvent со свойствами Id, ActorId, TypeId, Date, ObjectId и хеш- таблицей дополнительных пар ключ / значение Details. Это представлено в базе данных таблицей StreamEvent (Id, ActorId, TypeId, Date, ObjectId) и таблицей StreamEventDetails (StreamEventId, DetailKey, DetailValue).

ActorId, TypeId и ObjectId позволяют захватывать событие Subject-Verb-Object (и позднее запрашивать). Каждое действие может привести к созданию нескольких экземпляров StreamEvent.

Затем я создал подкласс для StreamEvent для каждого типа события, например LoginEvent, PictureCommentEvent. Каждый из этих подклассов имеет больше специфических для контекста свойств, таких как PictureId, ThumbNail, CommenText и т. Д. (Все, что требуется для события), которые фактически хранятся в виде пар ключ / значение в таблице hashtable/StreamEventDetail.

При извлечении этих событий из базы данных я использую фабричный метод (на основе TypeId), чтобы создать правильный класс StreamEvent.

Каждый подкласс StreamEvent имеет метод Render (context As StreamContext), который выводит событие на экран на основе переданного класса StreamContext. Класс StreamContext позволяет устанавливать параметры в зависимости от контекста представления. Если вы посмотрите на Facebook, например, в вашей ленте новостей на главной странице перечислены полные имена (и ссылки на их профили) всех участников каждого действия, тогда как при просмотре ленты друзей вы видите только их имена (но и полные имена других актеров).,

Я еще не реализовал агрегированный канал (домашняя страница Facebook), но я представляю, что создам таблицу AggregateFeed с полями UserId, StreamEventId, которая заполняется на основе какого-то алгоритма "Хммм, вы можете найти этот интересный" алгоритм.

Любые комментарии будут высоко оценены.

// одна запись на фактическое событие
События {
  идентификатор, метка времени, тип, данные
}

// одна запись на событие, на канал, содержащий это событие
events_feeds {
  event_id, feed_id
}

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

Если вы решите, что вы собираетесь реализовать в Rails, возможно, вы найдете следующий плагин полезным:

ActivityStreams: http://github.com/face/activity_streams/tree/master

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

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

Как упомянуто выше, это может столкнуться с проблемами масштабируемости по мере роста сайта. Лично меня сейчас не волнуют проблемы масштабирования. Я буду беспокоиться об этом позже.

Очевидно, что Facebook проделал большую работу по масштабированию, поэтому я бы порекомендовал вам прочитать их технический блог, так как он содержит массу отличного контента -> http://www.facebook.com/notes.php?id=9445547199

Я искал лучшие решения, чем денормализованная таблица, о которой я упоминал выше. Другой способ, который я нашел для достижения этой цели, состоит в том, чтобы объединить весь контент, который будет в данном потоке активности, в одну строку. Он может храниться в XML, JSON или в каком-либо сериализованном формате, который может быть прочитан вашим приложением. Процесс обновления тоже будет простым. После выполнения действия поместите новое действие в очередь (возможно, используя Amazon SQS или что-то еще), а затем постоянно опрашивайте очередь на предмет следующего элемента. Возьмите этот элемент, проанализируйте его и поместите его содержимое в соответствующий объект канала, хранящийся в базе данных.

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

Надеюсь это поможет!:)

Есть два сообщения о такой активности:

Эти решения не включают в себя все ваши требования, но они должны дать вам некоторые идеи.

Я думаю, что подход Плурка интересен: они предоставляют всю вашу временную шкалу в формате, который очень похож на графики акций Google Finance.

Возможно, стоит взглянуть на Нина, чтобы увидеть, как работает социальная сеть. Страницы разработчика выглядят особенно полезными.

Я решил это несколько месяцев назад, но я думаю, что моя реализация слишком проста.
Я создал следующие модели:

HISTORY_TYPE

ID           - The id of the history type
NAME         - The name (type of the history)
DESCRIPTION  - A description

HISTORY_MESSAGES

ID
HISTORY_TYPE - A message of history belongs to a history type
MESSAGE      - The message to print, I put variables to be replaced by the actual values

HISTORY_ACTIVITY

ID
MESSAGE_ID    - The message ID to use
VALUES        - The data to use

пример

MESSAGE_ID_1 => "User %{user} created a new entry"
ACTIVITY_ID_1 => MESSAGE_ID = 1, VALUES = {user: "Rodrigo"}

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

Моя компания Collabinate ( http://www.collabinate.com/) выросла из этой реализации, и мы реализовали масштабируемый, высокопроизводительный движок потоков операций поверх графической базы данных для достижения этой цели. Мы фактически использовали вариант алгоритма Graphity (адаптированный из ранней работы @RenePickhardt, который также дал здесь ответ) для создания движка.

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

Другие вопросы по тегам