实现社交活动流的最佳方式是什么?

我很想听听你对实现社交活动流的最佳方式(Facebook就是最著名的例子)的看法。涉及的问题/挑战有:

  • 不同类型的活动(张贴,评论..)
  • 不同类型的对象(帖子,评论,照片..)
  • 1-n个不同角色的用户(“用户x回复了用户y对用户Z帖子的评论”)
  • 同一活动项的不同视图(“您评论了..”vs。“你的朋友x评论”vs。"用户x评论说.."3个“评论”活动的表示)

.. 还有更多,特别是如果你把它提高到一个高度复杂的水平,比如,把几个活动项目合并成一个(“用户x, y和z评论了那张照片”)。

任何关于模式、论文等关于最灵活、有效和强大的方法来实现这样一个系统、数据模型等的想法或建议都将受到欢迎。

尽管大多数问题与平台无关,但我最终有可能在Ruby on Rails上实现这样一个系统

53218 次浏览

我认为Plurk的方法很有趣:他们以一种看起来很像谷歌财经的股票图表的格式提供你的整个时间轴。

可能值得一看来看看社交网络是如何工作的。开发人员页面看起来特别有用。

事件流最大的问题是可见性和性能;您需要将显示的事件限制为只显示该特定用户感兴趣的事件,并且需要保持整理和识别这些事件所需的时间。我建立了一个小型的社交网络;我发现,在小范围内,在数据库中保留“事件”表是可行的,但在中等负载下就会出现性能问题。

对于较大的消息流和用户,最好使用消息传递系统,将事件作为消息发送到单个配置文件。这意味着您不能很容易地订阅人们的事件流,也不能很容易地查看以前的事件,但是当您需要为特定用户呈现流时,您只是呈现了一小组消息。

我相信这是Twitter最初的设计缺陷——我记得读到过他们正在访问数据库,以提取并过滤他们的事件。这一切都与架构有关,与Rails无关,(不幸的是)Rails产生了“ruby不能伸缩”的梗。我最近看到一个演示,其中开发人员使用Amazon的简单排队服务作为他们的消息后端,用于一个类似twitter的应用程序,该应用程序具有更高的扩展能力——如果您的负载足够高,可能值得将SQS作为系统的一部分。

我创建了这样一个系统,我采取了这样的方法:

数据库表中包含以下列:id、userId、类型、数据、时间。

  • 用户标识是生成活动的用户
  • 类型是活动的类型(即写博客文章,添加照片,评论用户的照片)
  • 数据是一个带有活动元数据的序列化对象,你可以在其中放入任何你想要的东西

这限制了用户、时间和活动类型的搜索/查找,但在facebook类型的活动提要中,这并不是真正的限制。如果表上的索引正确,则查找结果为

在这种设计中,您必须决定每种类型的事件需要什么样的元数据。例如,新照片的feed活动可以是这样的:

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

可以看到,虽然照片的名称肯定存储在包含照片的其他表中,我可以从那里检索名称,但我将在元数据字段中复制名称,因为如果想加快速度,您不希望对其他数据库表进行任何连接。为了显示50个不同用户的200个不同事件,你需要速度。

然后我有一些类,这些类扩展了一个基本的FeedActivity类,用于呈现不同类型的活动条目。事件分组也将在呈现代码中构建,以避免数据库的复杂性。

// one entry per actual event
events {
id, timestamp, type, data
}


// one entry per event, per feed containing that event
events_feeds {
event_id, feed_id
}
当事件被创建时,决定它出现在哪个提要中,并将它们添加到events_feeds中。 要获取提要,请从events_feeds中选择,加入事件,按时间戳排序。 然后可以对该查询的结果进行过滤和聚合。 使用此模型,您可以在创建事件后更改事件属性,而不需要额外的工作

我使用了与heyman类似的方法——一个非规范化的表,其中包含将在给定的活动流中显示的所有数据。它适用于活动有限的小型站点。

如上所述,随着站点的增长,它很可能面临可伸缩性问题。就我个人而言,我现在并不担心规模问题。我以后再考虑这个问题。

Facebook显然在扩展方面做得很好,所以我建议你阅读他们的工程博客,因为它有大量的好内容-> http://www.facebook.com/notes.php?id=9445547199

我一直在寻找比上面提到的非规格化表更好的解决方案。我发现的另一种实现此目的的方法是将给定活动流中的所有内容浓缩到一行中。它可以存储为XML、JSON或应用程序可以读取的某种序列化格式。更新过程也很简单。在进行活动时,将新活动放入队列(可能使用Amazon SQS或其他东西),然后不断轮询队列以查找下一项。获取该项,解析它,并将其内容放在存储在数据库中的适当提要对象中。

这种方法的优点是,每当请求特定提要时,您只需要读取一个数据库表,而不是获取一系列表。此外,它允许您维护一个有限的活动列表,因为每当您更新列表时,您可能会弹出最古老的活动项。

希望这能有所帮助!:)

如果你决定用Rails实现,也许你会发现下面的插件很有用:

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

如果没有其他问题,您将从数据模型和为推拉活动提供的API两方面来研究一个实现。

我昨天开始执行一个这样的系统,这就是我要做的…

我创建了一个具有IdActorId类型id日期ObjectId属性的StreamEvent类和一个包含额外细节键/值对的哈希表。这在数据库中由一个StreamEvent表(IdActorId类型id日期ObjectId)和一个Id3表(Id4, Id5, Id6)表示。

ActorId类型idObjectId允许捕获一个主语-动词-对象事件(以及稍后查询)。每个操作都可能导致创建多个StreamEvent实例。

然后,我为StreamEvent的每种类型的事件创建了一个子类,例如LoginEventPictureCommentEvent。这些子类中的每一个都有更多上下文特定的属性,如PictureId缩略图CommenText等(事件所需的任何属性),这些属性实际上存储为hashtable/StreamEventDetail表中的键/值对。

当从数据库中提取这些事件时,我使用一个工厂方法(基于类型id)来创建正确的StreamEvent类。

StreamEvent的每个子类都有一个Render(上下文 As StreamContext)方法,该方法根据传递的StreamContext类将事件输出到屏幕。StreamContext类允许基于视图的上下文设置选项。以Facebook为例,主页上的信息流会列出参与每个行动的每个人的全名(以及他们个人资料的链接),而查看朋友的信息流,你只能看到他们的名字(但其他参与者的全名)。

我还没有实现一个聚合提要(Facebook家),但我想我会创建一个AggregateFeed表,其中有字段用户标识StreamEventId,这是基于某种'嗯,你可能会发现这个有趣'算法填充。

任何意见都将非常感激。

我在几个月前解决了这个问题,但我认为我的实现太基本了 我创建了以下模型:

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

这是一个很好的演示,概述了Etsy.com是如何构建他们的活动流的。这是我在这个主题上找到的最好的例子,尽管它不是特定于rails的。

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

如果您愿意使用单独的软件,我建议使用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/ < a href = " http://www.rene-pickhardt.de/graphity-an-efficient-graph-model-for-retrieving-the-top-k-news-feeds-for-users-in-social-networks/ " > < / >

在上面的链接中,您可以找到屏幕视频和这种方法的基准测试(显示graphity能够每秒检索超过10k个流)。

关于这样的活动流有两个铁路广播:

这些解决方案不包括你所有的要求,但它应该给你一些想法。

在几个应用程序中实现了活动流以支持社交提要、微博和协作功能之后,我意识到基本功能非常常见,可以转换为通过API使用的外部服务。如果您正在将流构建到生产应用程序中,并且没有独特的或非常复杂的需求,那么使用经过验证的服务可能是最好的方法。对于生产应用程序,我绝对推荐使用这种方法,而不是在关系数据库上使用自己的简单解决方案。

我的公司collaboration (http://www.collabinate.com)就是从这种实现中发展出来的,我们已经在图形数据库之上实现了一个可伸缩的高性能活动流引擎来实现它。实际上,我们使用了Graphity算法的变体(改编自@RenePickhardt的早期工作,他也在这里提供了答案)来构建引擎。

如果您希望自己托管引擎或需要特殊功能,其核心代码实际上是非商业用途的开源代码,因此欢迎您查看。

我们开放了我们的方法: https://github.com/tschellenbach/Stream-Framework 它是目前最大的开源库,旨在解决这个问题

构建Stream Framework的同一团队还提供了一个托管API,为您处理复杂性。有可用于Node, Python, Rails和PHP的客户端。

另外,看看这篇高可伸缩性的文章,我们解释了一些涉及到的设计决策: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic-feeds.html < / p >

本教程将帮助你使用Redis设置一个像Pinterest的提要这样的系统。这很容易上手。

要了解更多关于feed设计的知识,我强烈建议阅读一些我们基于Feedly的文章:

虽然Stream Framework是基于Python的,但从Ruby应用程序中使用它并不太难。你可以简单地将它作为服务运行,并在它前面插入一个小的http API。我们正在考虑添加一个API来从其他语言访问Feedly。不过现在你得扮演你自己的角色。