Jaki jest najlepszy sposób wdrożenia strumienia aktywności społecznościowej? [Zamknięte]

265

Chciałbym usłyszeć twoje opinie, w jaki sposób najlepiej wdrożyć strumień aktywności społecznościowej (Facebook jest najbardziej znanym przykładem). Problemami / wyzwaniami są:

  • Różne rodzaje działań (publikowanie, komentowanie ...)
  • Różne typy obiektów (post, komentarz, zdjęcie ...)
  • 1-n użytkowników pełniących różne role („Użytkownik x odpowiedział na komentarz Użytkownika y do postu użytkownika Z”)
  • Różne poglądy na ten sam element działania („skomentowałeś ..” vs. „Twój przyjaciel x skomentował” vs. „użytkownik x skomentował ..” => 3 przedstawienia działania „skomentuj”)

.. i kilka innych, zwłaszcza jeśli podniosłeś poziom zaawansowania, jak Facebook, na przykład, łącząc kilka elementów aktywności w jeden („skomentowali to zdjęcie użytkownicy x, yiz” ”

Docenione zostaną wszelkie przemyślenia lub wskazówki na temat wzorów, dokumentów itp. Na temat najbardziej elastycznych, wydajnych i wydajnych podejść do wdrażania takiego systemu, modelu danych itp.

Chociaż większość problemów dotyczy platformy, istnieje prawdopodobieństwo, że skończę wdrażać taki system na Ruby on Rails

Jon Seigel
źródło

Odpowiedzi:

143

Stworzyłem taki system i przyjąłem takie podejście:

Tabela bazy danych z następującymi kolumnami: identyfikator, identyfikator użytkownika, typ, dane, czas.

  • userId to użytkownik, który wygenerował działanie
  • typ to rodzaj działania (np. napisał wpis na blogu, dodał zdjęcie, skomentował zdjęcie użytkownika)
  • dane są serializowanym obiektem z metadanymi dla działania, w którym można umieścić cokolwiek chcesz

Ogranicza to wyszukiwanie / wyszukiwanie, które możesz robić w kanałach, do użytkowników, czasu i rodzajów aktywności, ale w kanale aktywności typu Facebook nie jest to tak naprawdę ograniczające. Przy prawidłowych indeksach na stole wyszukiwania są szybkie .

Dzięki takiemu projektowi będziesz musiał zdecydować, jakie metadane powinny wymagać każdego typu zdarzenia. Na przykład aktywność kanału dla nowego zdjęcia może wyglądać mniej więcej tak:

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

Widać, że chociaż nazwa zdjęcia z pewnością jest przechowywana w innej tabeli zawierającej zdjęcia, i mógłbym stamtąd je odzyskać, powielę nazwę w polu metadanych, ponieważ nie chcesz tego robić wszelkie sprzężenia w innych tabelach bazy danych, jeśli chcesz prędkości. Aby wyświetlić, powiedzmy 200, różne zdarzenia od 50 różnych użytkowników, potrzebujesz prędkości.

Następnie mam klasy, które rozszerzają podstawową klasę FeedActivity do renderowania różnych typów pozycji działań. Grupowanie zdarzeń byłoby również wbudowane w kod renderujący, aby uniknąć złożoności bazy danych.

Cześć, stary
źródło
3
Tak, zgadza się. Ostatnio korzystałem z MongoDB ( mongodb.org ) w kilku projektach, których podejście bez schematów sprawia, że ​​bardzo dobrze nadaje się do tworzenia dobrze działającego strumienia aktywności społecznościowej, który jest zgodny z tym projektem.
heyman
6
TheApprentice: Tak, możesz również podać pole nazwy użytkownika. W naszym systemie wyświetlaliśmy tylko zdarzenia wygenerowane przez znajomych użytkownika i uważam, że mieliśmy już w pamięci mapę identyfikatora użytkownika-> nazwy użytkownika, więc wyszukiwanie nazw użytkowników nie wymagało JOIN i było szybkie.
heyman
2
Trzeba by to załatwić ręcznie. Prawdopodobnie najlepiej to zrobić, gdy zdjęcie zostanie usunięte (znajdź element kanału w kanale użytkownika i usuń / zaktualizuj go).
heyman
21
Nie bardzo rozumiem, co jest takiego wspaniałego w tej odpowiedzi? Jak utworzenie prostej tabeli przekłada się na ważony kanał aktywności podobny do Facebooka? Wszystko, co robi, to przechowywanie całej aktywności. Co wciąż pozostawia pytanie, jak przekształcić tabelę danych w dynamiczny ważony plik danych o aktywności?
ChuckKelly,
4
@ChuckKelly: Jeśli dobrze pamiętam, w 2008 roku, kiedy napisałem odpowiedź, kanał na Facebooku wcale nie był ważony. To był tylko chronologiczny kanał z całą aktywnością twoich przyjaciół.
hejman
117

To bardzo dobra prezentacja przedstawiająca sposób, w jaki Etsy.com zaprojektował strumienie swojej działalności. To najlepszy przykład, jaki znalazłem na ten temat, choć nie jest on specyficzny dla szyn.

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

Mark Kennedy
źródło
21
^^ Ponieważ musisz wrócić do SO po odwiedzeniu strony. lol
Stephen Corwin
1
Świetna prezentacja, która szczegółowo wyjaśnia, jak system działa na prawdziwej stronie o dużym ruchu.
ramirami,
44

Otworzyliśmy nasze źródło: https://github.com/tschellenbach/Stream-Framework Jest to obecnie największa biblioteka open source mająca na celu rozwiązanie tego problemu.

Ten sam zespół, który zbudował Stream Framework, oferuje również hostowany interfejs API, który obsługuje złożoność. Spójrz na getstream.io Istnieją klienci dla Node, Python, Rails i PHP.

Ponadto zapoznaj się z tym postem dotyczącym wysokiej skalowalności, w którym wyjaśniliśmy niektóre decyzje związane z projektowaniem: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic- feeds.html

Ten samouczek pomoże Ci skonfigurować system taki jak kanał Pinterest za pomocą Redis. Rozpoczęcie jest dość łatwe.

Aby dowiedzieć się więcej o projektowaniu kanałów, zdecydowanie polecam przeczytanie niektórych artykułów, na których bazowaliśmy Feedly:

Chociaż Stream Framework jest oparty na języku Python, nie byłoby zbyt trudne w użyciu z aplikacji Ruby. Możesz po prostu uruchomić go jako usługę i przykleić przed nim mały interfejs API HTTP. Rozważamy dodanie interfejsu API, aby uzyskać dostęp do Feedly z innych języków. W tej chwili będziesz jednak musiał odgrywać własną rolę.

Thierry
źródło
19

Największe problemy ze strumieniami wydarzeń to widoczność i wydajność; musisz ograniczyć liczbę wyświetlanych zdarzeń, aby były interesujące tylko dla danego użytkownika, i musisz zachować czas potrzebny na uporządkowanie i identyfikację tych zdarzeń. Zbudowałem niewielką sieć społecznościową; Przekonałem się, że w małych skalach utrzymywanie tabeli „zdarzeń” w bazie danych działa, ale może to być problem z wydajnością przy umiarkowanym obciążeniu.

Przy większym strumieniu wiadomości i użytkowników najlepiej jest wybrać system przesyłania wiadomości, w którym zdarzenia są wysyłane jako wiadomości do poszczególnych profili. Oznacza to, że nie możesz łatwo subskrybować strumieni wydarzeń osób i bardzo łatwo zobaczyć poprzednie zdarzenia, ale po prostu renderujesz małą grupę wiadomości, gdy chcesz wyrenderować strumień dla konkretnego użytkownika.

Myślę, że to była oryginalna wada projektowa Twittera. Pamiętam, że czytałem, że odwiedzali bazę danych, aby pobrać i odfiltrować swoje zdarzenia. Miało to wszystko wspólnego z architekturą i nie miało nic wspólnego z Railsami, które (niestety) dały początek memowi „ruby nie skaluje”. Niedawno widziałem prezentację, w której programista wykorzystał prostą usługę kolejki Amazon jako zaplecze przesyłania wiadomości dla aplikacji podobnej do Twittera, która miałaby znacznie wyższe możliwości skalowania - warto spojrzeć na SQS jako część systemu, jeśli obciążenia są wystarczająco wysokie .

Tim Howland
źródło
Tim, czy przypadkiem pamiętasz nazwę prezentacji lub prezentera?
Danita
miało to miejsce w prezentacji Oreilly and Associate Ignite Boston, zarówno pod numerem 3, jak i czwartym. Wydaje mi się, że prezenter miał książkę o skalowaniu RoR z Oreilly. Przepraszam, nie mogę być bardziej szczegółowy!
Tim Howland
Dzięki Tim :) A propos, co miałeś na myśli mówiąc o „małej sieci społecznościowej”? Ilu użytkowników lub aktywnych użytkowników w danym momencie?
Danita
3
Jeśli ktoś tego potrzebuje, myślę, że jest to prezentacja, o której mówi Tim: „Dan Chak - Skalowanie do rozmiaru twoich problemów” radar.oreilly.com/2008/09/ignite-boston-4----videos -uplo.html
Danita
Mały w tym przypadku jest taki, że „wybierz * ze zdarzeń, w których zdarzenie jest widoczne dla tego użytkownika” zwraca wynik w wartości zdarzeń krótszej niż sekunda lub dwie cyfry o wartości kilkuset tysięcy wierszy.
Tim Howland
12

Jeśli chcesz użyć oddzielnego oprogramowania, sugeruję serwer Graphity, który dokładnie rozwiązuje problem dotyczący strumieni aktywności (w oparciu o bazę danych wykresu neo4j).

Algorytmy zostały zaimplementowane jako samodzielny serwer REST, dzięki czemu możesz hostować własny serwer, aby dostarczać strumienie aktywności: http://www.rene-pickhardt.de/graphity-server-for-social-activity-streams-released-gplv3 /

W pracy i teście porównawczym pokazałem, że pobieranie strumieni wiadomości zależy tylko liniowo od ilości elementów, które chcesz odzyskać, bez zbędnej redundancji danych:

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

Na powyższym linku znajdują się screencasty i test porównawczy tego podejścia (pokazujący, że graficzność jest w stanie pobrać więcej niż 10 000 strumieni na sekundę).

Rene Pickhardt
źródło
10

Wczoraj zacząłem wdrażać taki system, tutaj muszę ...

I stworzył StreamEvent klasę z właściwości Id , ActorId , TypeId , Data , ObjectId i hashtable dodatkowych Szczegóły par klucz / wartość. To jest reprezentowana w bazie danych przez StreamEvent tabeli ( Id , ActorId , TypeId , Date , objectID ) i StreamEventDetails tabeli ( StreamEventId , DetailKey , DetailValue ).

ActorId , TypeId i ObjectId pozwalają na wydarzenie Przedmiot Verb-Object do niewoli (i później zapytaliśmy). Każda akcja może spowodować utworzenie kilku instancji StreamEvent.

Następnie utworzyłem podklasę dla każdego typu zdarzenia StreamEvent , np. LoginEvent , PictureCommentEvent . Każda z tych podklas ma bardziej specyficzne dla kontekstu właściwości, takie jak PictureId , ThumbNail , CommenText itp. (Cokolwiek jest wymagane dla zdarzenia), które są faktycznie przechowywane jako pary klucz / wartość w tabeli hashtable / StreamEventDetail.

Podczas pobierania tych zdarzeń z powrotem z bazy danych używam metody fabrycznej (na podstawie TypeId ), aby utworzyć poprawną klasę StreamEvent.

Każda podklasa StreamEvent ma metodę renderowania (w kontekście jako StreamContext ), która wysyła zdarzenie do ekranu na podstawie przekazanej klasy StreamContext . Klasa StreamContext umożliwia ustawianie opcji na podstawie kontekstu widoku. Jeśli np. Spojrzysz na Facebooka, Twój kanał informacyjny na stronie głównej zawiera pełne nazwy (i linki do ich profilu) wszystkich osób zaangażowanych w każdą akcję, podczas gdy patrząc na kanał znajomego, widzisz tylko jego imię (ale pełne nazwiska innych aktorów) .

Nie wdrożyłem jeszcze zagregowanego kanału (strona główna Facebook), ale wyobrażam sobie, że utworzę tabelę AggregateFeed, która zawiera pola UserId , StreamEventId, które są zapełniane na podstawie pewnego rodzaju algorytmu „Hmmm, możesz znaleźć ten interesujący” algorytm.

Wszelkie komentarze będą mile widziane.

jammus
źródło
Pracuję nad systemem takim jak ten. Jestem bardzo zainteresowany jakąkolwiek wiedzą na jego temat, czy kiedykolwiek skończyłeś swój?
JasonDavis,
Świetna odpowiedź! Doskonałe rozdzielenie problemów, czyste i eleganckie!
Mosh
To dobry początek! Jest bardzo podobny do tego, jak zacząłem wdrażać mój pierwszy strumień. Jednak po przejściu do zbiorczego pliku danych sprawy szybko się komplikują. Masz rację, że potrzebujesz solidnego algorytmu. Moje poszukiwania doprowadziły mnie do algorytmu Rene Pickhardta (mówi o tym w swojej odpowiedzi tutaj), który następnie wdrożyłem do mojej własnej usługi, która jest teraz komercyjna (więcej informacji na stronie collabinate.com i mojej odpowiedzi na to pytanie).
Mafuba,
10
// jeden wpis na rzeczywiste zdarzenie
wydarzenia {
  identyfikator, znacznik czasu, typ, dane
}

// jeden wpis na zdarzenie, na kanał zawierający to wydarzenie
events_feeds {
  identyfikator_zdarzenia, identyfikator_ feeda
}

Gdy wydarzenie zostanie utworzone, zdecyduj, w których kanałach się pojawi, i dodaj je do kanałów_źródła. Aby uzyskać kanał, wybierz z wydarzenia wydarzenia_feeds, dołącz do wydarzeń, uporządkuj według datownika. Następnie można przeprowadzić filtrowanie i agregację wyników tego zapytania. W tym modelu możesz zmienić właściwości zdarzenia po utworzeniu bez dodatkowej pracy.

jedediah
źródło
1
Załóżmy, że po dodaniu wydarzenia ktoś został dodany jako przyjaciel, który musi zobaczyć to wydarzenie w swoim kanale? to nie zadziałałoby
Joshua Kissoon
8

Jeśli zdecydujesz się wdrożyć w Railsach, być może przydatna będzie następująca wtyczka:

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

Jeśli nic więcej, przyjrzysz się implementacji, zarówno pod względem modelu danych, jak i interfejsu API przewidzianego do wypychania i wyciągania działań.

Alderete
źródło
6

Miałem podobne podejście do hejma - zdormalizowana tabela zawierająca wszystkie dane, które byłyby wyświetlane w danym strumieniu aktywności. Działa dobrze w przypadku małej witryny o ograniczonej aktywności.

Jak wspomniano powyżej, w miarę rozwoju witryny prawdopodobnie napotkają problemy ze skalowalnością. Osobiście nie martwię się teraz problemami skalowania. Będę się tym martwić później.

Facebook najwyraźniej wykonał świetną robotę skalowania, więc polecam przeczytanie ich blogu inżynieryjnego, ponieważ ma mnóstwo świetnych treści -> http://www.facebook.com/notes.php?id=9445547199

Szukałem lepszych rozwiązań niż tabela zdenormalizowana, o której wspomniałem powyżej. Innym sposobem na osiągnięcie tego celu jest zagęszczenie całej zawartości, która byłaby w danym strumieniu aktywności w jednym wierszu. Może być przechowywany w formacie XML, JSON lub innym formacie zserializowanym, który może być odczytany przez twoją aplikację. Proces aktualizacji również byłby prosty. Po aktywności umieść nową aktywność w kolejce (być może za pomocą Amazon SQS lub czegoś innego), a następnie stale przeszukuj kolejkę w poszukiwaniu następnego elementu. Chwyć ten element, przeanalizuj go i umieść jego zawartość w odpowiednim obiekcie kanału przechowywanym w bazie danych.

Zaletą tej metody jest to, że wystarczy odczytać tylko jedną tabelę bazy danych za każdym razem, gdy żądany jest ten konkretny kanał, zamiast pobierać serię tabel. Pozwala także zachować skończoną listę działań, ponieważ możesz wyskoczyć z najstarszego elementu działania przy każdej aktualizacji listy.

Mam nadzieję że to pomoże! :)


źródło
Dokładnie moje myśli, potrzebowałem tylko potwierdzenia moich myśli, które prawdopodobnie mam teraz, na zdrowie!
Sohail,
5

Istnieją dwa komunikaty o takim strumieniu aktywności:

Te rozwiązania nie obejmują wszystkich twoich wymagań, ale powinny dać ci kilka pomysłów.

Benjamin Crouzier
źródło
1
PublicActivity jest świetny i może obsłużyć wszystkie przypadki użycia w pytaniu.
DaveStephens 30.09.13
3

Myślę, że podejście Plurka jest interesujące: dostarczają całą oś czasu w formacie przypominającym wykresy giełdowe Google Finance.

Warto spojrzeć na Ning, aby zobaczyć, jak działa sieć społecznościowa. Strony programistów wyglądają szczególnie pomocne.

królikarnia
źródło
2

Rozwiązałem to kilka miesięcy temu, ale myślę, że moja implementacja jest zbyt podstawowa.
Stworzyłem następujące modele:

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

Przykład

MESSAGE_ID_1 => "User %{user} created a new entry"
ACTIVITY_ID_1 => MESSAGE_ID = 1, VALUES = {user: "Rodrigo"}
Rodrigo
źródło
2

Po wdrożeniu strumieni aktywności w celu włączenia kanałów społecznościowych, mikroblogowania i funkcji współpracy w kilku aplikacjach, zdałem sobie sprawę, że podstawowa funkcjonalność jest dość powszechna i może zostać przekształcona w usługę zewnętrzną, z której korzystasz za pośrednictwem interfejsu API. Jeśli budujesz strumień w aplikacji produkcyjnej i nie masz wyjątkowych lub bardzo złożonych potrzeb, skorzystanie ze sprawdzonej usługi może być najlepszym rozwiązaniem. Zdecydowanie poleciłbym to w aplikacjach produkcyjnych, a nie po dodaniu własnego prostego rozwiązania do relacyjnej bazy danych.

Moja firma Collabinate ( http://www.collabinate.com ) wyrosła z tej realizacji. Aby to osiągnąć, wdrożyliśmy skalowalny, wysokowydajny silnik strumienia aktywności na bazie graficznej bazy danych. W rzeczywistości wykorzystaliśmy wariant algorytmu Graphity (dostosowany od wczesnej pracy @RenePickhardt, który również udzielił odpowiedzi tutaj) do zbudowania silnika.

Jeśli chcesz hostować silnik samodzielnie lub potrzebujesz specjalistycznej funkcjonalności, kod podstawowy to tak naprawdę open source do celów niekomercyjnych, więc zapraszamy do obejrzenia.

Mafuba
źródło