Wie lässt sich ein sozialer Aktivitätenstrom am besten implementieren?

Ich bin daran interessiert, Ihre Meinungen zu hören, in denen der beste Weg ist, einen sozialen Aktivitäten-Stream zu implementieren (Facebook ist das bekannteste Beispiel). Probleme / Herausforderungen sind:

  • Verschiedene Arten von Aktivitäten (Posten, Kommentieren …)
  • Verschiedene Arten von Objekten (Post, Kommentar, Foto ..)
  • 1-n Benutzer mit unterschiedlichen Rollen (“Benutzer x hat auf den Kommentar von User y auf den Z-Beitrag des Benutzers geantwortet”)
  • Verschiedene Ansichten des gleichen Aktivitätselements (“Sie kommentierten …” vs. “Ihr Freund x kommentierte” vs. “Benutzer x kommentierte ..” => 3 Darstellungen einer “Kommentar” -Aktivität)

.. und einige mehr, vor allem, wenn Sie es auf ein hohes Niveau der Raffinesse bringen, wie Facebook, zum Beispiel, mehrere Aktivitätselemente zu einem kombiniert (“Benutzer x, y und z hat das Foto kommentiert”)

Irgendwelche Gedanken oder Hinweise auf Muster, Papiere, etc. zu den flexibelsten, effizientesten und leistungsfähigsten Ansätzen zur Implementierung eines solchen Systems, Datenmodells etc. wären willkommen.

Obwohl die meisten Probleme plattformunabhängig sind, habe ich die Chance, ein solches System auf Ruby on Rails zu implementieren

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

    databasetabelle mit den folgenden Spalten: id, userId, type, data, time.

    • userId ist der Benutzer, der die Aktivität generiert hat
    • type ist der Typ der Aktivität (zB Geschriebener Blogbeitrag, Foto hinzugefügt, Foto des Benutzers kommentiert)
    • Daten sind ein serialisiertes Objekt mit Metadaten für die Aktivität, in die Sie alles einfügen können, was Sie wollen

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

    Bei diesem Design müssen Sie entscheiden, welche Metadaten für welche Art von Ereignis benötigt werden. Eine Feedaktivität für ein neues Foto könnte beispielsweise so 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 sicherlich in einer anderen Tabelle gespeichert ist, die die Fotos enthält, und ich den Namen von dort abrufen könnte, werde ich den Namen im Metadatenfeld duplizieren, weil Sie nicht möchten Alle Verknüpfungen in anderen databasetabellen, wenn Sie Geschwindigkeit wünschen. Und um 200 verschiedene Ereignisse von 50 verschiedenen Benutzern anzuzeigen, brauchen Sie Geschwindigkeit.

    Dann habe ich classn, die eine grundlegende FeedActivity-class zum Rendern der verschiedenen Typen von Aktivitätseinträgen erweitern. Die Gruppierung von Ereignissen würde ebenfalls in den Rendering-Code integriert werden, um die Komplexität der database zu vermeiden.

    Dies ist eine sehr gute Präsentation, in der beschrieben wird, wie Etsy ihre Aktivitätenströme gestaltet hat. Es ist das beste Beispiel, das ich zu dem Thema gefunden habe, obwohl es nicht railsspezifisch ist.

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

    Wir haben unseren Ansatz auf Open Source bezogen: https://github.com/tschellenbach/Stream-Framework Derzeit ist es die größte Open-Source-Bibliothek, die dieses Problem lösen soll.

    Das gleiche Team, das Stream Framework entwickelt hat, bietet auch eine gehostete API, die die Komplexität für Sie übernimmt. Werfen Sie einen Blick auf getstream.io Es gibt Clients für Node, Python, Rails und PHP.

    Schauen Sie sich auch diesen hohen Skalierbarkeits-Beitrag an, in dem wir einige der Designentscheidungen erläutern: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic- fütterungen.html

    Dieses Tutorial hilft Ihnen, ein System wie Pinterest mit Redis einzurichten. Es ist ziemlich einfach, mit anzufangen.

    Um mehr über das Design von Futtermitteln zu erfahren, empfehle ich dringend, einige der Artikel zu lesen, auf denen wir basieren:

    • Yahoo Forschungsbericht
    • Twitter 2013 Redis basiert , mit Fallback
    • Cassandra bei Instagram
    • Etsy-Feed-Skalierung
    • Facebook-Geschichte
    • Django-Projekt mit guten Namenskonventionen. (Aber nur database)
    • http://activitystrea.ms/specs/atom/1.0/ (Schauspieler, Verb, Objekt, Ziel)
    • Quora post zu Best Practices
    • Quora skaliert einen sozialen Netzwerk-Feed
    • Redis Ruby Beispiel
    • FriendFeed-Ansatz
    • Thoonk-Einrichtung
    • Twitter’s Ansatz

    Obwohl Stream Framework auf Python basiert, wäre es nicht zu schwierig, es in einer Ruby-App zu verwenden. Sie könnten es einfach als Dienst ausführen und eine kleine http API davor halten. Wir überlegen, eine API für den Zugriff auf Feedly aus anderen Sprachen hinzuzufügen. Im Moment musst du deine eigene Rolle spielen.

    Die größten Probleme bei Ereignisströmen sind Sichtbarkeit und performance. Sie müssen die angezeigten Ereignisse auf diejenigen beschränken, die nur für den jeweiligen Benutzer interessant sind, und Sie müssen die Zeit, die zum Sortieren und Identifizieren dieser Ereignisse benötigt wird, im Griff behalten. Ich habe ein kleineres soziales Netzwerk aufgebaut; Ich habe festgestellt, dass bei kleinen Skalen das Speichern einer “Ereignis” -Tabelle in einer database funktioniert, dass es jedoch unter mittlerer Last zu einem performancesproblem kommt.

    Bei einem größeren Datenstrom 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 nicht einfach die Ereignisströme von Personen abonnieren und vorherige Ereignisse sehr einfach anzeigen können. Sie können jedoch nur eine kleine Gruppe von Meldungen rendern, wenn Sie den Stream für einen bestimmten Benutzer rendern müssen.

    Ich glaube, das war der ursprüngliche Konstruktionserrors von Twitter – ich erinnere mich, dass ich gelesen habe, dass sie die database angriffen, um ihre Ereignisse einzublenden und zu filtern. Das hatte alles mit Architektur zu tun und nichts mit Rails zu tun, was (leider) das “Ruby Scale Scale Meme” hervorbrachte. 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 weit höhere Skalierungsfähigkeiten hat. Es kann sich lohnen, SQS als Teil Ihres Systems zu betrachten, wenn Ihre Lasten hoch genug sind .

    Wenn Sie bereit sind, eine separate Software zu verwenden, empfehle ich den Graphity-Server, der genau das Problem für Activity-Streams triggers (aufbauend auf der Neo4j-Graph-database).

    Die Algorithmen wurden als eigenständiger REST-Server implementiert, so dass Sie Ihren eigenen Server zur Bereitstellung von Activity Streams hosten können: http://www.rene-pickhardt.de/graphity-server-for-social-activity-streams-released-gplv3 /

    In dem Papier und der Benchmark habe ich gezeigt, dass das Abrufen von Nachrichtenströmen nur linear von der Menge der Elemente abhängt, die Sie abrufen möchten, ohne dass Sie durch die Denormalisierung der Daten Redundanz erhalten würden:

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

    Auf dem obigen Link finden Sie Screencasts und einen Benchmark dieses Ansatzes (der zeigt, dass Graphity mehr als 10k Streams pro Sekunde abrufen kann).

    Ich habe gestern mit der Implementierung eines solchen Systems begonnen, hier muss ich …

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

    ActorId , TypeId und ObjectId ermöglichen, dass ein Subject-Verb-Object-Ereignis erfasst (und später abgefragt) wird. 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 hat mehr kontextspezifische 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.

    Beim Zurückziehen dieser Ereignisse aus der database verwende ich eine Factory-Methode (basierend auf TypeId ), um die richtige StreamEvent-class zu erstellen.

    Jede Unterklasse von StreamEvent verfügt über eine Render ( context As StreamContext ) -Methode, die das Ereignis basierend auf der übergebenen StreamContext- class ausgibt . Die StreamContext-class ermöglicht das Festlegen von Optionen basierend auf dem Kontext der Ansicht. Wenn Sie beispielsweise auf Facebook schauen, werden in Ihrem News-Feed auf der Startseite die Vollnamen (und Links zu ihrem Profil) aller an der Aktion beteiligten Personen angezeigt. Wenn Sie den Feed eines Freundes sehen, sehen Sie nur deren Vornamen (aber die vollständigen Namen anderer Akteure). .

    Ich habe noch keinen aggregierten Feed implementiert (Facebook home), aber ich stelle mir vor, dass ich eine AggregateFeed- Tabelle mit den Feldern UserId und StreamEventId erstellen werde, die auf einer Art “Hmmm, Sie könnten diesen interessanten Algorithmus finden” basieren.

    Alle Kommentare würden sehr geschätzt werden.

     // ein Eintrag pro tatsächliches Ereignis
     Veranstaltungen {
       ID, Zeitstempel, Typ, Daten
     }
    
     // ein Eintrag pro Ereignis und pro Feed, der dieses Ereignis enthält
     events_feeds {
       Ereignis-ID, Feed-ID
     }
    

    Wenn das Ereignis erstellt wurde, 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 events_feeds, treten Sie den Ereignissen bei und ordnen Sie sie nach Zeitstempel. Die Filterung und Aggregation kann dann für die Ergebnisse dieser Abfrage durchgeführt werden. Mit diesem Modell können Sie die Ereigniseigenschaften nach der Erstellung ohne zusätzliche Arbeit ändern.

    Wenn Sie entscheiden, dass Sie in Rails implementieren werden, finden Sie möglicherweise das folgende Plug-in nützlich:

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

    Nicht zuletzt werden Sie eine Implementierung sowohl im Hinblick auf das Datenmodell als auch auf die API für Push- und Pull-Aktivitäten sehen.

    Ich hatte einen ähnlichen Ansatz wie Heyman – eine denormalisierte Tabelle, die alle Daten enthält, die in einem bestimmten Activity-Stream angezeigt würden. Es funktioniert gut für eine kleine Website mit eingeschränkter Aktivität.

    Wie oben erwähnt, ist es wahrscheinlich, dass Skalierbarkeitsprobleme auftreten, wenn die Site wächst. Persönlich mache ich mir im Moment keine Sorgen über Skalierungsprobleme. Darüber werde ich mir zu einem späteren Zeitpunkt Gedanken machen.

    Facebook hat offensichtlich einen großartigen Job bei der Skalierung gemacht, deshalb würde ich empfehlen, dass Sie ihren Technik-Blog lesen, da er eine Menge toller Inhalte hat -> http://www.facebook.com/notes.php?id=9445547199

    Ich habe nach besseren Lösungen gesucht als die oben erwähnte denormalisierte Tabelle. Eine andere Möglichkeit, dies zu erreichen, besteht darin, den gesamten Inhalt, der in einem gegebenen Aktivitätsstrom enthalten sein würde, in einer einzelnen Zeile zu verdichten. Es könnte in XML, JSON oder einem serialisierten Format gespeichert werden, das von Ihrer Anwendung gelesen werden könnte. Der Update-process wäre auch einfach. Fügen Sie nach der Aktivität die neue Aktivität in eine Warteschlange ein (möglicherweise unter Verwendung von Amazon SQS oder einem anderen Programm) und überprüfen Sie dann die Warteschlange für das nächste Element. Greifen Sie auf dieses Element zu, analysieren Sie es, und platzieren Sie seinen Inhalt in dem entsprechenden Feedobjekt, das in der database gespeichert ist.

    Das Gute an dieser Methode ist, dass Sie nur dann eine einzelne databasetabelle lesen müssen, wenn dieser bestimmte Feed angefordert wird, anstatt eine Reihe von Tabellen zu erfassen. Außerdem können Sie eine endliche Liste von Aktivitäten verwalten, da Sie beim Aktualisieren der Liste immer das älteste Aktivitätselement abbrechen können.

    Hoffe das hilft! 🙂

    Es gibt zwei Railscasts über einen solchen Activity Stream:

    Diese Lösungen enthalten nicht alle Ihre Anforderungen, aber es sollte Ihnen einige Ideen geben.

    Ich denke, dass Plurks Ansatz interessant ist: Sie liefern Ihre gesamte Timeline in einem Format, das stark an die Aktienkurse von Google Finance erinnert.

    Es kann sich lohnen, sich Ning anzusehen, um zu sehen, wie ein Netzwerk für soziale Netzwerke funktioniert. Die Entwicklerseiten sehen besonders hilfreich aus.

    Ich habe das vor ein paar Monaten getriggers, aber ich denke, meine Implementierung ist zu einfach.
    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"} 

    Nach dem Implementieren von Activity-Streams zum Aktivieren von Social-Feeds, Microblogging und Collaboration-functionen in mehreren Anwendungen wurde mir klar, dass die Basisfunktionalität recht häufig ist und in einen externen Service umgewandelt werden kann, den Sie über eine API nutzen. Wenn Sie den Stream in einer Produktionsanwendung erstellen und keine einzigartigen oder zu komplexen Anforderungen haben, ist die Verwendung eines bewährten Service möglicherweise der beste Weg. Ich würde dies definitiv für Produktionsanwendungen empfehlen, anstatt eine eigene einfache Lösung auf eine relationale database zu setzen.

    Meine Firma Collabinate ( http://www.collabinate.com ) ist aus dieser Erkenntnis heraus entstanden, und wir haben eine skalierbare, leistungsstarke Activity Stream-Engine auf einer Graphdatenbank implementiert, um dies zu erreichen. Wir haben tatsächlich eine Variante des Graphity-Algorithmus verwendet (der aus der frühen Arbeit von @RenePickhardt stammt, die auch hier eine Antwort gegeben hat), um die Engine zu bauen.

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