Quelle est la meilleure façon de mettre en œuvre un flux d'activités sociales? [fermé]


265

Je suis intéressé à entendre vos opinions sur la meilleure façon de mettre en œuvre un flux d'activités sociales (Facebook est l'exemple le plus célèbre). Les problèmes / défis impliqués sont:

  • Différents types d'activités (affichage, commentaire ..)
  • Différents types d'objets (post, commentaire, photo ..)
  • Utilisateurs 1-n impliqués dans différents rôles ("L'utilisateur x a répondu au commentaire de l'utilisateur y sur le message Z de l'utilisateur")
  • Différentes vues du même élément d'activité ("vous avez commenté .." vs "votre ami x a commenté" vs "utilisateur x a commenté .." => 3 représentations d'une activité "commentaire")

.. et quelques autres, surtout si vous le prenez à un niveau élevé de sophistication, comme Facebook le fait, par exemple, en combinant plusieurs éléments d'activité en un seul ("les utilisateurs x, y et z ont commenté cette photo"

Toute réflexion ou indication sur les modèles, les articles, etc. sur les approches les plus flexibles, efficaces et puissantes pour la mise en œuvre d'un tel système, modèle de données, etc. serait appréciée.

Bien que la plupart des problèmes soient indépendants de la plate-forme, il est probable que je finisse par implémenter un tel système sur Ruby on Rails

Réponses:


143

J'ai créé un tel système et j'ai adopté cette approche:

Table de base de données avec les colonnes suivantes: id, userId, type, data, time.

  • userId est l'utilisateur qui a généré l'activité
  • type est le type d'activité (c.-à-d. rédiger un article de blog, ajouter une photo, commenter la photo de l'utilisateur)
  • les données sont un objet sérialisé avec des métadonnées pour l'activité où vous pouvez mettre ce que vous voulez

Cela limite les recherches / recherches, que vous pouvez faire dans les flux, les utilisateurs, le temps et les types d'activité, mais dans un flux d'activité de type Facebook, ce n'est pas vraiment limitatif. Et avec des indices corrects sur la table, les recherches sont rapides .

Avec cette conception, vous devriez décider des métadonnées que chaque type d'événement devrait nécessiter. Par exemple, une activité de flux pour une nouvelle photo pourrait ressembler à ceci:

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

Vous pouvez voir que, bien que le nom de la photo soit certainement stocké dans un autre tableau contenant les photos, et que je puisse récupérer le nom à partir de là, je vais dupliquer le nom dans le champ de métadonnées, parce que vous ne voulez pas faire toute jointure sur d'autres tables de base de données si vous voulez de la vitesse. Et pour afficher, disons 200, différents événements de 50 utilisateurs différents, vous avez besoin de vitesse.

Ensuite, j'ai des classes qui étend une classe FeedActivity de base pour rendre les différents types d'entrées d'activité. Le regroupement des événements serait également intégré au code de rendu, pour éloigner la complexité de la base de données.


3
Oui, c'est vrai. Dernièrement, j'ai utilisé MongoDB ( mongodb.org ) dans quelques projets, dont l'approche sans schéma le rend très approprié pour créer un flux d'activités sociales performant qui suit cette conception.
heyman

6
TheApprentice: Oui, vous pouvez également ajouter un champ de nom d'utilisateur. Dans notre système, nous n'affichions que les événements générés par les amis d'un utilisateur, et je pense que nous avions déjà une carte de l'ID utilisateur -> nom d'utilisateur des amis en mémoire, donc la recherche des noms d'utilisateur ne nécessitait pas de JOIN et était rapide.
heyman

2
Vous devrez gérer ce cas manuellement. Il est probablement préférable de le faire lorsque la photo est supprimée (recherchez l'élément de flux dans le flux de l'utilisateur et supprimez / mettez-le à jour).
heyman

21
Je ne comprends pas vraiment ce qui est si génial avec cette réponse? Comment la création d'un tableau simple se traduit-elle par un flux d'activité pondéré similaire à Facebook? Tout ce qu'il fait, c'est stocker toute l'activité. Ce qui laisse encore la question de savoir comment transformer une table de données en un flux d'activité pondéré dynamique?
ChuckKelly

4
@ChuckKelly: Si je me souviens bien, en 2008, lorsque j'ai écrit la réponse, le flux Facebook n'était pas du tout pondéré. C'était juste un flux chronologique avec toutes les activités de vos amis.
heyman

117

Il s'agit d'une très bonne présentation décrivant comment Etsy.com a architecturé leurs flux d'activités. C'est le meilleur exemple que j'ai trouvé sur le sujet, bien qu'il ne soit pas spécifique aux rails.

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


21
^^ Parce que vous devez revenir à SO après avoir visité le site. lol
Stephen Corwin

1
Grande présentation qui explique en détail le fonctionnement du système sur un véritable site Web à fort trafic.
ramirami

44

Nous avons ouvert notre approche: https://github.com/tschellenbach/Stream-Framework C'est actuellement la plus grande bibliothèque open source visant à résoudre ce problème.

La même équipe qui a construit Stream Framework propose également une API hébergée, qui gère la complexité pour vous. Jetez un œil à getstream.io Des clients sont disponibles pour Node, Python, Rails et PHP.

De plus, jetez un œil à ce poste à haute évolutivité où nous expliquons certaines des décisions de conception impliquées: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic- feeds.html

Ce didacticiel vous aidera à configurer un système comme le flux Pinterest à l'aide de Redis. C'est assez facile de commencer.

Pour en savoir plus sur la conception de flux, je recommande fortement de lire certains des articles sur lesquels nous avons basé Feedly:

Bien que Stream Framework soit basé sur Python, il ne serait pas trop difficile à utiliser à partir d'une application Ruby. Vous pouvez simplement l'exécuter en tant que service et coller une petite API http devant lui. Nous envisageons d'ajouter une API pour accéder à Feedly à partir d'autres langues. Pour le moment, vous devrez jouer votre propre rôle.


19

Les problèmes les plus importants avec les flux d'événements sont la visibilité et les performances; vous devez restreindre les événements affichés pour qu'ils soient uniquement intéressants pour cet utilisateur particulier, et vous devez garder le temps nécessaire pour trier et identifier ces événements gérables. J'ai construit un petit réseau social; J'ai trouvé qu'à petite échelle, la conservation d'une table "d'événements" dans une base de données fonctionne, mais que cela devient un problème de performances sous une charge modérée.

Avec un flux de messages et d'utilisateurs plus important, il est probablement préférable d'utiliser un système de messagerie, où les événements sont envoyés sous forme de messages à des profils individuels. Cela signifie que vous ne pouvez pas vous abonner facilement aux flux d'événements des gens et voir les événements précédents très facilement, mais vous restituez simplement un petit groupe de messages lorsque vous devez rendre le flux pour un utilisateur particulier.

Je crois que c'était le défaut de conception d'origine de Twitter - je me souviens avoir lu qu'ils frappaient la base de données pour récupérer et filtrer leurs événements. Cela avait tout à voir avec l'architecture et rien à voir avec Rails, ce qui (malheureusement) a donné naissance au mème "ruby does scale". J'ai récemment vu une présentation où le développeur a utilisé le service de file d'attente simple d'Amazon comme back-end de messagerie pour une application de type Twitter qui aurait des capacités de mise à l'échelle beaucoup plus élevées - cela peut valoir la peine d'étudier SQS dans le cadre de votre système, si vos charges sont suffisamment élevées .


Tim, vous souvenez-vous par hasard du nom de la présentation ou du présentateur?
Danita

c'était lors de la présentation Ignite Boston d'Oreilly and Associate, numéro 3 ou 4. Je pense que le présentateur avait un livre sur la mise à l'échelle du RoR avec Oreilly. Désolé, je ne peux pas être plus précis!
Tim Howland,

Merci Tim :) Au fait, que vouliez-vous dire par "petit réseau social"? Combien d'utilisateurs ou d'utilisateurs actifs à un certain moment?
Danita

3
Au cas où quelqu'un en aurait besoin, je pense que c'est la présentation dont Tim parle: "Dan Chak - Dimensionner à la taille de vos problèmes" radar.oreilly.com/2008/09/ignite-boston-4----videos -uplo.html
Danita

Dans ce cas, la taille est telle que «sélectionner * parmi les événements où event.is est visible pour cet utilisateur» renvoie un résultat en moins d'une seconde ou deux chiffres pour quelques centaines de milliers de lignes d'événements.
Tim Howland,

12

Si vous êtes prêt à utiliser un logiciel séparé, je suggère le serveur Graphity qui résout exactement le problème des flux d'activité (en s'appuyant sur la base de données du graphique neo4j).

Les algorithmes ont été mis en œuvre en tant que serveur REST autonome afin que vous puissiez héberger votre propre serveur pour fournir des flux d'activité: http://www.rene-pickhardt.de/graphity-server-for-social-activity-streams-released-gplv3 /

Dans le document et le benchmark, j'ai montré que la récupération des flux d'actualités dépend uniquement de la quantité d'éléments que vous souhaitez récupérer sans aucune redondance que vous obtiendriez en dénormalisant les données:

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

Sur le lien ci-dessus, vous trouverez des screencasts et une référence de cette approche (montrant que la graphité est capable de récupérer plus de 10 000 flux par seconde).


10

J'ai commencé à mettre en œuvre un système comme celui-ci hier, voici où je dois ...

J'ai créé une classe StreamEvent avec les propriétés Id , ActorId , TypeId , Date , ObjectId et une table de hachage de paires clé / valeur Détails supplémentaires . Ceci est représenté dans la base de données par un StreamEvent tableau ( Id , ActorID , TypeId , date , ObjectId ) et un StreamEventDetails tableau ( StreamEventId , DetailKey , DetailValue ).

Le ActorID , TypeId et ObjectId permettent un événement sujet-verbe-objet à saisir (et a demandé plus tard). Chaque action peut entraîner la création de plusieurs instances StreamEvent.

J'ai ensuite créé une sous-classe pour StreamEvent pour chaque type d'événement, par exemple LoginEvent , PictureCommentEvent . Chacune de ces sous-classes a des propriétés plus spécifiques au contexte telles que PictureId , ThumbNail , CommenText , etc. (tout ce qui est requis pour l'événement) qui sont réellement stockées sous forme de paires clé / valeur dans la table de hachage / StreamEventDetail.

Lorsque je retire ces événements de la base de données, j'utilise une méthode d'usine (basée sur TypeId ) pour créer la classe StreamEvent correcte.

Chaque sous-classe de StreamEvent a une méthode Render ( context As StreamContext ) qui génère l'événement à filtrer en fonction de la classe StreamContext passée . La classe StreamContext permet de définir des options en fonction du contexte de la vue. Si vous regardez Facebook par exemple, votre flux d'actualités sur la page d'accueil répertorie les noms complets (et les liens vers leur profil) de toutes les personnes impliquées dans chaque action, tandis qu'en regardant le flux d'un ami, vous ne voyez que leur prénom (mais les noms complets des autres acteurs) .

Je n'ai pas encore implémenté de flux agrégé (page d'accueil Facebook) mais j'imagine que je vais créer une table AggregateFeed qui contient les champs UserId , StreamEventId qui sont remplis en fonction d'une sorte d'algorithme `` Hmmm, vous pourriez trouver cet algorithme intéressant ''.

Tout commentaire serait grandement apprécié.


Je travaille sur un système comme celui-ci, je suis très intéressé par toute connaissance à ce sujet, avez-vous déjà terminé le vôtre?
JasonDavis

Très bonne réponse! Excellente séparation des préoccupations, propre et élégante!
Mosh

C'est un bon début! C'est très similaire à la façon dont j'ai commencé à implémenter mon premier flux. Cependant, une fois que vous avez accédé au flux global, les choses commencent à se compliquer rapidement. Vous avez raison, vous avez besoin d'un algorithme robuste. Ma recherche m'a conduit à l'algorithme de René Pickhardt (il en parle dans sa réponse ici), que j'ai ensuite implémenté dans mon propre service, qui est maintenant commercial (voir collabinate.com et ma réponse sur cette question pour plus).
Mafuba

10
// une entrée par événement réel
événements {
  id, horodatage, type, données
}

// une entrée par événement, par flux contenant cet événement
events_feeds {
  event_id, feed_id
}

Lorsque l'événement est créé, décidez dans quels flux il apparaît et ajoutez-les à events_feeds. Pour obtenir un flux, sélectionnez parmi events_feeds, rejoignez des événements, triez par horodatage. Le filtrage et l'agrégation peuvent ensuite être effectués sur les résultats de cette requête. Avec ce modèle, vous pouvez modifier les propriétés de l'événement après la création sans travail supplémentaire.


1
Supposons que quelqu'un d'autre soit ajouté comme ami après l'ajout de l'événement, qui doit voir cet événement dans son flux? alors cela ne fonctionnerait pas
Joshua Kissoon

8

Si vous décidez d'implémenter dans Rails, vous trouverez peut-être le plugin suivant utile:

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

Si rien d'autre, vous aurez à regarder une implémentation, à la fois en termes de modèle de données, ainsi que l'API fournie pour les activités de poussée et de traction.


6

J'avais une approche similaire à celle de Heyman - un tableau dénormalisé contenant toutes les données qui seraient affichées dans un flux d'activité donné. Cela fonctionne bien pour un petit site avec une activité limitée.

Comme mentionné ci-dessus, il est susceptible de rencontrer des problèmes d'évolutivité à mesure que le site se développe. Personnellement, je ne suis pas préoccupé par les problèmes de mise à l'échelle en ce moment. Je m'en inquiéterai plus tard.

Facebook a évidemment fait un excellent travail de mise à l'échelle, donc je vous recommande de lire leur blog d'ingénierie, car il a une tonne de contenu formidable -> http://www.facebook.com/notes.php?id=9445547199

J'ai cherché de meilleures solutions que le tableau dénormalisé que j'ai mentionné ci-dessus. Une autre façon que j'ai trouvée d'accomplir cela est de condenser tout le contenu qui serait dans un flux d'activité donné sur une seule ligne. Il peut être stocké au format XML, JSON ou dans un format sérialisé pouvant être lu par votre application. Le processus de mise à jour serait également simple. Lors de l'activité, placez la nouvelle activité dans une file d'attente (peut-être en utilisant Amazon SQS ou autre chose), puis interrogez continuellement la file d'attente pour l'élément suivant. Saisissez cet élément, analysez-le et placez son contenu dans l'objet de flux approprié stocké dans la base de données.

La bonne chose à propos de cette méthode est que vous n'avez besoin de lire qu'une seule table de base de données chaque fois que ce flux particulier est demandé, plutôt que de saisir une série de tables. En outre, il vous permet de maintenir une liste finie d'activités car vous pouvez supprimer l'élément d'activité le plus ancien chaque fois que vous mettez à jour la liste.

J'espère que cela t'aides! :)


Exactement mes pensées, j'avais juste besoin d'une validation de mes pensées que j'ai probablement maintenant, cheers!
Sohail


3

Je pense que l' approche de Plurk est intéressante: ils fournissent l'intégralité de votre chronologie dans un format qui ressemble beaucoup aux graphiques boursiers de Google Finance.

Il peut être utile de regarder Ning pour voir comment fonctionne un réseau de réseautage social. Les pages des développeurs semblent particulièrement utiles.


2

J'ai résolu cela il y a quelques mois, mais je pense que ma mise en œuvre est trop basique.
J'ai créé les modèles suivants:

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

Exemple

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

2

Après avoir implémenté des flux d'activités pour activer les fonctionnalités de flux social, de microblogage et de collaboration dans plusieurs applications, j'ai réalisé que la fonctionnalité de base est assez courante et pourrait être transformée en un service externe que vous utilisez via une API. Si vous intégrez le flux à une application de production et n'avez pas de besoins uniques ou profondément complexes, l'utilisation d'un service éprouvé peut être la meilleure solution. Je recommanderais certainement ceci pour des applications de production au-dessus de rouler votre propre solution simple au-dessus d'une base de données relationnelle.

Mon entreprise Collabinate ( http://www.collabinate.com ) est née de cette réalisation, et nous avons mis en place un moteur de flux d'activité évolutif et hautes performances au-dessus d'une base de données graphique pour y parvenir. Nous avons en fait utilisé une variante de l'algorithme Graphity (adapté des premiers travaux de @RenePickhardt qui a également fourni une réponse ici) pour construire le moteur.

Si vous souhaitez héberger le moteur vous-même ou avez besoin de fonctionnalités spécialisées, le code principal est en fait open source à des fins non commerciales, vous pouvez donc y jeter un coup d'œil.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.