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

260

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

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

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

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

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

Теги:
android-activity
social-networking

13 ответов

134

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

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

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

Это ограничивает поиск/поиск, вы можете делать в каналах, для пользователей, времени и видов деятельности, но в фиде активности типа 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 для рендеринга различных типов записей активности. Группировка событий будет также построена в коде рендеринга, чтобы убрать сложность из базы данных.

  • 1
    это действительно отличная система. Я предполагаю, что вы создаете записи базы данных каналов в то же время, когда фактически выполняете действие, например, создаете новую запись события комментария в таблице каналов в то же время, когда пользователь отправляет комментарий
  • 3
    Да, это правильно. В последнее время я использовал MongoDB ( mongodb.org ) в нескольких проектах, чей бессхемный подход делает его очень подходящим для создания хорошо выполняемого потока социальной активности, который следует этому дизайну.
Показать ещё 23 комментария
116

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

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

  • 21
    ^^ Потому что вы должны вернуться в SO после посещения сайта. лол
  • 1
    Отличная презентация, которая подробно объясняет, как система работает на реальном веб-сайте с большим трафиком.
39

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

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

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

Этот учебник поможет вам настроить систему, такую ​​как Pinterest feed, используя Redis. С этим легко начать.

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

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

18

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

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

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

  • 0
    Тим, вы случайно не помните название презентации или докладчика?
  • 0
    это было на презентации Ognilly and Associate Ignite Boston, номер 3 или 4 - я думаю, что у докладчика была книга о масштабировании RoR с Oreilly. Извините, я не могу быть более конкретным!
Показать ещё 3 комментария
11

Если вы хотите использовать отдельное программное обеспечение, я предлагаю сервер 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 тыс. потоков в секунду).

10

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

Я создал класс StreamEvent со свойствами Id, ActorId, TypeId, Date, ObjectId и хэш-таблицей дополнительных пар сведений/значений. Это представлено в базе данных таблицей 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.

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

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

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

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

  • 0
    Я работаю над такой системой, я очень заинтересован в каких-либо знаниях по ней, ты когда-нибудь заканчивал свою?
  • 0
    Отличный ответ! Отличное разделение забот, чисто и элегантно!
Показать ещё 1 комментарий
10
// one entry per actual event
events {
  id, timestamp, type, data
}

// one entry per event, per feed containing that event
events_feeds {
  event_id, feed_id
}

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

  • 1
    Предположим, что кто-то еще был добавлен в друзья после добавления события, кому нужно видеть это событие в своей ленте? тогда это не сработает
8

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

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

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

6

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

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

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

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

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

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

  • 0
    Именно мои мысли, мне просто нужно было проверить мои мысли, которые я, вероятно, получил сейчас, ура!
5

Есть два railscasts о таком потоке активности:

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

  • 1
    PublicActivity великолепно и может обрабатывать все варианты использования в вопросе.
3

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

Возможно, стоит посмотреть Ning, чтобы узнать, как работает сеть социальных сетей. Особенно полезны страницы developer.

2

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

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

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

2

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

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"}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню