Was ist die beste Art, einen sozialen Aktivitätsstrom zu implementieren? [geschlossen]


265

Ich bin daran interessiert, Ihre Meinung zu hören, wie Sie einen Stream für soziale Aktivitäten am besten implementieren können (Facebook ist das bekannteste Beispiel). Probleme / Herausforderungen sind:

  • Verschiedene Arten von Aktivitäten (Posten, Kommentieren ..)
  • Verschiedene Arten von Objekten (Beitrag, Kommentar, Foto ..)
  • 1-n Benutzer, die an verschiedenen Rollen beteiligt sind ("Benutzer x hat auf den Kommentar von Benutzer y zum Z-Beitrag des Benutzers geantwortet")
  • Unterschiedliche Ansichten desselben Aktivitätselements ("Sie haben kommentiert .." vs. "Ihr Freund x hat kommentiert" vs. "Benutzer x hat kommentiert .." => 3 Darstellungen einer Aktivität "Kommentar")

.. und einige mehr, insbesondere wenn Sie ein hohes Maß an Raffinesse erreichen, wie Facebook beispielsweise mehrere Aktivitätselemente zu einem kombiniert ("Benutzer x, y und z haben dieses Foto kommentiert").

Alle Gedanken oder Hinweise zu Mustern, Papieren usw. zu den flexibelsten, effizientesten und leistungsfähigsten Ansätzen zur Implementierung eines solchen Systems, Datenmodells usw. sind willkommen.

Obwohl die meisten Probleme plattformunabhängig sind, besteht die Möglichkeit, dass ich ein solches System auf Ruby on Rails implementiere

Antworten:


143

Ich habe ein solches System geschaffen und diesen Ansatz gewählt:

Datenbanktabelle mit den folgenden Spalten: ID, Benutzer-ID, Typ, Daten, Zeit.

  • userId ist der Benutzer, der die Aktivität generiert hat
  • Typ ist der Typ der Aktivität (dh Blogpost geschrieben, Foto hinzugefügt, Foto des Benutzers kommentiert)
  • data ist ein serialisiertes Objekt mit Metadaten für die Aktivität, in die Sie alles eingeben können, was Sie möchten

Dies beschränkt die Suchvorgänge / Suchvorgänge, die Sie in den Feeds durchführen können, auf Benutzer, Zeit- und Aktivitätstypen. In einem Aktivitätsfeed vom Typ Facebook ist dies jedoch nicht wirklich einschränkend. Und mit den richtigen Indizes auf dem Tisch sind die Suchvorgänge schnell .

Bei diesem Entwurf müssten Sie entscheiden, welche Metadaten für jeden Ereignistyp erforderlich sind. Eine Feed-Aktivität für ein neues Foto könnte beispielsweise folgendermaßen aussehen:

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

Sie können sehen, dass, obwohl der Name des Fotos mit Sicherheit in einer anderen Tabelle mit den Fotos gespeichert ist und ich den Namen von dort abrufen könnte, ich den Namen im Metadatenfeld duplizieren werde, weil Sie dies nicht möchten Alle Verknüpfungen in anderen Datenbanktabellen, wenn Sie Geschwindigkeit wünschen. Und um beispielsweise 200 verschiedene Ereignisse von 50 verschiedenen Benutzern anzuzeigen, benötigen Sie Geschwindigkeit.

Dann habe ich Klassen, die eine grundlegende FeedActivity-Klasse zum Rendern der verschiedenen Arten von Aktivitätseinträgen erweitern. Eine Gruppierung von Ereignissen würde auch im Rendering-Code erstellt, um die Komplexität der Datenbank zu verringern.


3
Ja, das ist richtig. In letzter Zeit habe ich MongoDB ( mongodb.org ) in einigen Projekten verwendet, deren schemenloser Ansatz es sehr gut geeignet macht, einen leistungsfähigen Stream für soziale Aktivitäten zu erstellen, der diesem Entwurf folgt.
Heyman

6
TheApprentice: Ja, vielleicht möchten Sie auch ein Feld für den Benutzernamen eingeben. In unserem System haben wir nur Ereignisse angezeigt, die von Freunden eines Benutzers generiert wurden, und ich glaube, wir hatten bereits eine Karte mit der Benutzer-ID-> Benutzername der Freunde im Speicher, sodass das Nachschlagen der Benutzernamen keinen JOIN erforderte und schnell war.
Heyman

2
Sie müssten diesen Fall manuell behandeln. Es ist wahrscheinlich am besten, dies zu tun, wenn das Foto gelöscht wird (suchen Sie das Feed-Element im Feed des Benutzers und löschen / aktualisieren Sie es).
Heyman

21
Ich verstehe nicht ganz, was an dieser Antwort so toll ist? Wie wird das Erstellen einer einfachen Tabelle in einen gewichteten Aktivitäts-Feed ähnlich wie bei Facebook übersetzt? Er speichert nur die gesamte Aktivität. Was lässt noch die Frage offen, wie eine Datentabelle in einen dynamisch gewichteten Aktivitätsfeed umgewandelt werden kann?
ChuckKelly

4
@ChuckKelly: Wenn ich mich richtig erinnere, wurde der Facebook-Feed 2008, als ich die Antwort schrieb, überhaupt nicht gewichtet. Es war nur ein chronologischer Feed mit allen Aktivitäten Ihrer Freunde.
Heyman

117

Dies ist eine sehr gute Präsentation, in der dargelegt wird, wie Etsy.com ihre Aktivitätsströme aufgebaut hat. Es ist das beste Beispiel, das ich zu diesem Thema gefunden habe, obwohl es nicht spezifisch für Schienen ist.

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


21
^^ Weil du nach dem Besuch der Seite zu SO zurückkehren musst. lol
Stephen Corwin

1
Tolle Präsentation, die ausführlich erklärt, wie das System auf einer wirklich stark frequentierten Website funktioniert.
Ramirami

44

Wir haben unseren Ansatz als Open-Source-Lösung bereitgestellt : https://github.com/tschellenbach/Stream-Framework Es ist derzeit die größte Open-Source-Bibliothek zur Lösung dieses Problems.

Das gleiche Team, das Stream Framework erstellt hat, bietet auch eine gehostete API an, die die Komplexität für Sie übernimmt. Schauen Sie sich getstream.io an. Es gibt Clients für Node, Python, Rails und PHP.

Schauen Sie sich außerdem diesen Beitrag mit hoher Skalierbarkeit an, in dem wir einige der Entwurfsentscheidungen erläutern: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic- feeds.html

Dieses Tutorial hilft Ihnen dabei, ein System wie den Feed von Pinterest mit Redis einzurichten. Es ist ziemlich einfach, damit anzufangen.

Um mehr über das Feed-Design zu erfahren, empfehle ich dringend, einige der Artikel zu lesen, auf denen Feedly basiert:

Obwohl Stream Framework auf Python basiert, ist die Verwendung mit einer Ruby-App nicht allzu schwierig. Sie können es einfach als Dienst ausführen und eine kleine http-API davor stellen. Wir erwägen, eine API hinzuzufügen, um aus anderen Sprachen auf Feedly zuzugreifen. Im Moment musst du allerdings deine eigene Rolle spielen.


19

Die größten Probleme bei Ereignisströmen sind Sichtbarkeit und Leistung. Sie müssen die angezeigten Ereignisse so einschränken, dass sie nur für diesen bestimmten Benutzer interessant sind, und Sie müssen die Zeit beibehalten, die zum Sortieren und Identifizieren dieser Ereignisse erforderlich ist. Ich habe ein kleines soziales Netzwerk aufgebaut. Ich fand heraus, dass es in kleinen Maßstäben funktioniert, eine "Ereignistabelle" in einer Datenbank zu führen, aber dass es unter mäßiger Last zu einem Leistungsproblem wird.

Bei einem größeren Strom von Nachrichten und Benutzern ist es wahrscheinlich am besten, ein Nachrichtensystem zu verwenden, bei dem Ereignisse als Nachrichten an einzelne Profile gesendet werden. Dies bedeutet, dass Sie die Ereignisströme von Personen nicht einfach abonnieren und frühere Ereignisse sehr einfach anzeigen können. Sie rendern jedoch einfach eine kleine Gruppe von Nachrichten, wenn Sie den Stream für einen bestimmten Benutzer rendern müssen.

Ich glaube, dies war der ursprüngliche Designfehler von Twitter. Ich erinnere mich, dass sie die Datenbank aufgerufen haben, um ihre Ereignisse abzurufen und zu filtern. Dies hatte alles mit Architektur zu tun und nichts mit Rails, die (leider) das Mem "Ruby Don't Scale" hervorbrachten. Ich habe kürzlich eine Präsentation gesehen, in der der Entwickler den Simple Queue Service von Amazon als Messaging-Backend für eine Twitter-ähnliche Anwendung verwendet hat, die weitaus höhere Skalierungsfunktionen bietet. Es kann sich lohnen, SQS als Teil Ihres Systems zu betrachten, wenn Ihre Auslastung hoch genug ist .


Tim, erinnerst du dich zufällig an den Namen der Präsentation oder des Präsentators?
Danita

Es war bei der Präsentation von Oreilly und Associate's Ignite Boston, entweder Nummer 3 oder 4. Ich glaube, der Moderator hatte ein Buch über die Skalierung des RoR mit Oreilly. Entschuldigung, ich kann nicht genauer sein!
Tim Howland

Danke Tim :) Übrigens, was hast du mit "kleines soziales Netzwerk" gemeint? Wie viele Benutzer oder aktive Benutzer zu einem bestimmten Zeitpunkt?
Danita

3
Für den Fall, dass jemand es braucht, denke ich, dass dies die Präsentation ist, über die Tim spricht: "Dan Chak - Skalieren auf die Größe Ihrer Probleme" radar.oreilly.com/2008/09/ignite-boston-4----videos -uplo.html
Danita

Klein in diesem Fall ist, dass "Wählen Sie * aus Ereignissen aus, bei denen event.is für diesen Benutzer sichtbar ist" ein Ergebnis in weniger als einer Sekunde oder zweistelligen Ereignissen im Wert von einigen hunderttausend Zeilen zurückgibt.
Tim Howland

12

Wenn Sie bereit sind, eine separate Software zu verwenden, empfehle ich den Graphity-Server, der das Problem für Aktivitätsströme genau löst (basierend auf der neo4j-Grafikdatenbank).

Die Algorithmen wurden als eigenständiger REST-Server implementiert, sodass Sie Ihren eigenen Server hosten können, um Aktivitätsströme bereitzustellen: http://www.rene-pickhardt.de/graphity-server-for-social-activity-streams-released-gplv3 /.

In der Arbeit und im Benchmark habe ich gezeigt, dass das Abrufen von Nachrichtenströmen nur linear von der Anzahl der Elemente abhängt, die Sie abrufen möchten, ohne dass Redundanz durch Denormalisierung der Daten entsteht:

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

Unter dem obigen Link finden Sie Screencasts und einen Benchmark dieses Ansatzes (der zeigt, dass die Grafik mehr als 10.000 Streams pro Sekunde abrufen kann).


10

Ich habe gestern angefangen, ein solches System zu implementieren. Hier muss ich ...

Ich habe eine StreamEvent- Klasse mit den Eigenschaften Id , ActorId , TypeId , Date , ObjectId und einer Hashtabelle mit zusätzlichen Details- Schlüssel / Wert-Paaren erstellt. Dies wird in der Datenbank durch eine StreamEvent- Tabelle ( Id , ActorId , TypeId , Date , ObjectId ) und eine StreamEventDetails- Tabelle ( StreamEventId , DetailKey , DetailValue ) dargestellt.

Mit ActorId , TypeId und ObjectId kann ein Subject-Verb-Object-Ereignis erfasst (und später abgefragt) werden. Jede Aktion kann dazu führen, dass mehrere StreamEvent-Instanzen erstellt werden.

Ich habe dann eine Unterklasse für StreamEvent für jeden Ereignistyp erstellt, z . B. LoginEvent , PictureCommentEvent . Jede dieser Unterklassen verfügt über kontextspezifischere Eigenschaften wie PictureId , ThumbNail , CommenText usw. (was auch immer für das Ereignis erforderlich ist), die tatsächlich als Schlüssel / Wert-Paare in der Tabelle hashtable / StreamEventDetail gespeichert sind.

Wenn ich diese Ereignisse aus der Datenbank zurückziehe, verwende ich eine Factory-Methode (basierend auf der TypeId ), um die richtige StreamEvent-Klasse zu erstellen.

Jede Unterklasse von StreamEvent verfügt über eine Render- Methode ( Kontext als StreamContext ), die das Ereignis basierend auf der übergebenen StreamContext- Klasse auf dem Bildschirm ausgibt . Mit der StreamContext-Klasse können Optionen basierend auf dem Kontext der Ansicht festgelegt werden. Wenn Sie sich beispielsweise Facebook ansehen, werden in Ihrem Newsfeed auf der Startseite die vollständigen Namen (und Links zu ihrem Profil) aller an jeder Aktion Beteiligten aufgelistet. Wenn Sie sich den Feed eines Freundes ansehen, sehen Sie nur dessen Vornamen (aber die vollständigen Namen anderer Schauspieler). .

Ich habe noch keinen Aggregat-Feed (Facebook- Startseite) implementiert , aber ich kann mir vorstellen, dass ich eine AggregateFeed- Tabelle mit den Feldern UserId , StreamEventId erstellen werde , die basierend auf einer Art 'Hmmm, vielleicht finden Sie diesen interessanten' Algorithmus ausgefüllt.

Alle Kommentare wäre sehr dankbar.


Ich arbeite an einem System wie diesem und bin sehr an Wissen darüber interessiert. Haben Sie jemals Ihr System fertiggestellt?
JasonDavis

Gute Antwort! Hervorragende Trennung der Anliegen, sauber und elegant!
Mosh

Das ist ein guter Anfang! Es ist sehr ähnlich, wie ich mit der Implementierung meines ersten Streams begonnen habe. Sobald Sie jedoch zum aggregierten Feed gelangen, werden die Dinge schnell kompliziert. Sie haben Recht, dass Sie einen robusten Algorithmus benötigen. Meine Suche führte mich zu Rene Pickhardts Algorithmus (er spricht hier in seiner Antwort darüber), den ich dann in meinen eigenen Dienst implementierte, der jetzt kommerziell ist (siehe collabinate.com und meine Antwort auf diese Frage für weitere Informationen ).
Mafuba

10
// ein Eintrag pro aktuellem Ereignis
Veranstaltungen {
  ID, Zeitstempel, Typ, Daten
}}

// ein Eintrag pro Ereignis und Feed, der dieses Ereignis enthält
events_feeds {
  event_id, feed_id
}}

Wenn das Ereignis erstellt wird, entscheiden Sie, in welchen Feeds es angezeigt wird, und fügen Sie diese zu events_feeds hinzu. Um einen Feed zu erhalten, wählen Sie aus events_feeds aus, nehmen Sie an Ereignissen teil und sortieren Sie nach Zeitstempel. Die Ergebnisse dieser Abfrage können dann gefiltert und aggregiert werden. Mit diesem Modell können Sie die Ereigniseigenschaften nach der Erstellung ohne zusätzliche Arbeit ändern.


1
Angenommen, nach dem Hinzufügen des Ereignisses wird jemand anderes als Freund hinzugefügt, der dieses Ereignis in seinem Feed sehen muss. dann würde das nicht funktionieren
Joshua Kissoon

8

Wenn Sie sich für eine Implementierung in Rails entscheiden, ist das folgende Plugin möglicherweise hilfreich:

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

Wenn nichts anderes, können Sie sich eine Implementierung ansehen, sowohl hinsichtlich des Datenmodells als auch der API, die zum Push- und Pulling von Aktivitäten bereitgestellt wird.


6

Ich hatte einen ähnlichen Ansatz wie Heyman - eine denormalisierte Tabelle, die alle Daten enthält, die in einem bestimmten Aktivitätsstrom angezeigt werden. Es funktioniert gut für eine kleine Site mit begrenzter Aktivität.

Wie oben erwähnt, treten wahrscheinlich Skalierbarkeitsprobleme auf, wenn die Site wächst. Persönlich mache ich mir momentan keine Sorgen um die Skalierungsprobleme. Ich werde mich zu einem späteren Zeitpunkt darum kümmern.

Facebook hat offensichtlich großartige Arbeit bei der Skalierung geleistet, daher würde ich empfehlen, dass Sie den technischen Blog lesen, da er eine Menge großartiger Inhalte enthält -> http://www.facebook.com/notes.php?id=9445547199

Ich habe nach besseren Lösungen als der oben erwähnten denormalisierten Tabelle gesucht. Eine andere Möglichkeit, dies zu erreichen, besteht darin, den gesamten Inhalt eines bestimmten Aktivitätsstroms in einer einzigen Zeile zusammenzufassen. Es kann in XML, JSON oder einem serialisierten Format gespeichert sein, das von Ihrer Anwendung gelesen werden kann. Der Update-Prozess wäre auch einfach. Platzieren Sie die neue Aktivität nach der Aktivität in einer Warteschlange (möglicherweise mit Amazon SQS oder etwas anderem) und fragen Sie die Warteschlange dann kontinuierlich nach dem nächsten Element ab. Nehmen Sie das Element, analysieren Sie es und platzieren Sie den Inhalt in dem entsprechenden Feed-Objekt, das in der Datenbank gespeichert ist.

Das Gute an dieser Methode ist, dass Sie nur eine einzelne Datenbanktabelle lesen müssen, wenn dieser bestimmte Feed angefordert wird, anstatt eine Reihe von Tabellen abzurufen. Außerdem können Sie eine endliche Liste von Aktivitäten verwalten, da Sie bei jeder Aktualisierung der Liste möglicherweise das älteste Aktivitätselement entfernen.

Hoffe das hilft! :) :)


Genau meine Gedanken, ich brauchte nur eine Bestätigung meiner Gedanken, die ich jetzt wahrscheinlich bekommen habe, Prost!
Sohail


3

Ich denke Plurks Ansatz von interessant: Sie liefern Ihre gesamte Zeitleiste in einem Format, das den Aktiencharts von Google Finance sehr ähnlich sieht.

Es kann sich lohnen, sich Ning anzusehen, um zu sehen, wie ein soziales Netzwerk funktioniert. Die Entwicklerseiten sehen besonders hilfreich aus.


2

Ich habe das vor ein paar Monaten gelöst, aber ich denke, meine Implementierung ist zu grundlegend.
Ich habe folgende Modelle erstellt:

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

Beispiel

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

2

Nachdem ich Aktivitätsströme implementiert hatte, um Funktionen für soziale Feeds, Microblogging und Zusammenarbeit in mehreren Anwendungen zu aktivieren, wurde mir klar, dass die Basisfunktionalität weit verbreitet ist und in einen externen Dienst umgewandelt werden kann, den Sie über eine API verwenden. Wenn Sie den Stream in eine Produktionsanwendung einbauen und keine besonderen oder äußerst komplexen Anforderungen haben, ist die Verwendung eines bewährten Dienstes möglicherweise der beste Weg. Ich würde dies definitiv für Produktionsanwendungen empfehlen, indem Sie Ihre eigene einfache Lösung auf eine relationale Datenbank rollen.

Meine Firma Collabinate ( http://www.collabinate.com ) ist aus dieser Erkenntnis heraus , und wir haben eine skalierbare, leistungsstarke Aktivitäts-Stream-Engine auf einer implementiert, um dies zu erreichen. Wir haben tatsächlich eine Variante des Graphity-Algorithmus verwendet (angepasst an die frühen Arbeiten von @RenePickhardt, der auch hier eine Antwort lieferte), um die Engine zu erstellen.

Wenn Sie die Engine selbst hosten möchten oder spezielle Funktionen benötigen, ist der Kerncode tatsächlich Open Source für nichtkommerzielle Zwecke. Sie können also gerne einen Blick darauf werfen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.