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


265

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

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

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

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

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

Ответы:


143

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

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

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


3
Да, это правильно. В последнее время я использовал MongoDB ( mongodb.org ) в нескольких проектах, чей бессхемный подход делает его очень подходящим для создания хорошо выполняемого потока социальной активности, который следует этой схеме.
Хейман

6
TheApprentice: Да, вы также можете добавить поле имени пользователя. В нашей системе мы отображали только события, сгенерированные друзьями пользователя, и я считаю, что у нас уже была карта имени пользователя друзей -> имя пользователя в памяти, поэтому поиск имен пользователей не требовал JOIN и был быстрым.
Хейман

2
Вы должны были бы обработать этот случай вручную. Вероятно, лучше всего это сделать, когда фотография будет удалена (найдите элемент фида в ленте пользователя и удалите / обновите его).
Хейман

21
Я не совсем понимаю, что такого хорошего в этом ответе? Как создание простой таблицы переводится на взвешенный канал активности, аналогичный Facebook? Все, что он делает, это хранит всю активность. Что еще оставляет вопрос о том, как превратить таблицу данных в динамический взвешенный поток активности?
ChuckKelly

4
@ChuckKelly: Если я правильно помню, еще в 2008 году, когда я написал ответ, канал Facebook не был взвешен вообще. Это был просто хронологический канал со всей активностью ваших друзей.
Хейман

117

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

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


21
^^ Потому что вы должны вернуться в SO после посещения сайта. лол
Стивен Корвин

1
Отличная презентация, которая подробно объясняет, как система работает на реальном веб-сайте с большим трафиком.
Рамирами

44

Мы открыли исходный код нашего подхода: 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, с помощью Redis. Это довольно легко начать.

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

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


19

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

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

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


Тим, вы случайно не помните название презентации или докладчика?
Данита

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

Спасибо, Тим :) Кстати, что вы имели в виду под "маленькой социальной сетью"? Сколько пользователей или активных пользователей в определенное время?
Данита

3
В случае, если кому-то это нужно, я думаю, что это презентация, о которой говорит Тим: «Дэн Чак - масштабирование до размера ваших проблем» radar.oreilly.com/2008/09/ignite-boston-4----videos -uplo.html
Danita

Малый в этом случае таков, что «select * from events, где event.is видим для этого пользователя» возвращает результат менее чем за секунду или две цифры из нескольких сотен тысяч строк.
Тим Хоулэнд

12

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


10

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

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

ActorId , TypeId и ObjectId позволяют событие Тема-Глагол-Объект , который должен быть захвачен (а затем опрошена). Каждое действие может привести к созданию нескольких экземпляров StreamEvent.

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

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

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

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

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


Я работаю над такой системой, я очень заинтересован в каких-либо знаниях по ней, ты когда-нибудь заканчивал свою?
JasonDavis

Отличный ответ! Отличное разделение забот, чисто и элегантно!
Mosh

Это хорошее начало! Это очень похоже на то, как я начал реализовывать свой первый поток. Однако, как только вы доберетесь до совокупного фида, все станет быстро усложняться. Вы правы, что вам нужен надежный алгоритм. Мой поиск привел меня к алгоритму Рене Пикхардта (он говорит об этом в своем ответе здесь), который я затем внедрил в свой собственный сервис, который теперь является коммерческим (см. Collabinate.com и мой ответ на этот вопрос для получения дополнительной информации).
Мафуба

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

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

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


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

8

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

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

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


6

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

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

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

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

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

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


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

5

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

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


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

3

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

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


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

2

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.