270

我很想听听您的意见,哪些是实施社交活动流的最佳方式(Facebook 是最著名的例子)。涉及的问题/挑战是:

  • 不同类型的活动(发布,评论..)
  • 不同类型的对象(帖子、评论、照片..)
  • 1-n 个不同角色的用户(“用户 x 回复了用户 y 对用户 Z 帖子的评论”)
  • 同一活动项目的不同视图(“您评论了..”与“您的朋友 x 评论了”与“用户 x 评论了..”=>“评论”活动的 3 种表示)

.. 还有更多,特别是如果你把它提升到高水平,就像 Facebook 所做的那样,例如,将几个活动项目合并为一个(“用户 x、y 和 z 评论了那张照片”

任何关于模式、论文等关于实现此类系统、数据模型等的最灵活、最有效和最强大的方法的想法或指针将不胜感激。

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

4

13 回答 13

145

我已经创建了这样的系统,并采用了这种方法:

具有以下列的数据库表:id、userId、type、data、time。

  • userId是生成活动的用户
  • type是活动的类型(即写博客文章、添加照片、评论用户照片)
  • data是一个序列化对象,其中包含活动的元数据,您可以在其中放入任何您想要的内容

这限制了搜索/查找,您可以在提要中对用户、时间和活动类型进行,但在 facebook 类型的活动提要中,这并不是真正的限制。并且在表格上使用正确的索引,查找速度很快

使用这种设计,您必须决定每种类型的事件应该需要哪些元数据。例如,一张新照片的提要活动可能如下所示:

{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 类的类,用于呈现不同类型的活动条目。事件的分组也将在渲染代码中构建,以避免数据库的复杂性。

于 2008-10-15T16:45:31.913 回答
118

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

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

于 2011-02-16T21:34:26.167 回答
45

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

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

另外看看这篇高可扩展性帖子,我们解释了一些涉及的设计决策:http: //highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic-提要.html

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

要了解有关提要设计的更多信息,我强烈建议阅读我们基于提要的一些文章:

尽管 Stream Framework 是基于 Python 的,但在 Ruby 应用程序中使用它并不难。您可以简单地将其作为服务运行并在其前面粘贴一个小型 http API。我们正在考虑添加一个 API 以从其他语言访问 Feedly。目前,您必须扮演自己的角色。

于 2013-10-30T13:13:55.363 回答
19

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

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

我相信这是 Twitter 最初的设计缺陷——我记得读过他们正在访问数据库以提取和过滤他们的事件。这与架构有关,与 Rails 无关,Rails(不幸的是)催生了“ruby 无法扩展”的模因。我最近看到一个演示文稿,其中开发人员使用 Amazon 的Simple Queue Service作为他们的消息传递后端,用于类似 twitter 的应用程序,该应用程序具有更高的扩展能力 - 如果您的负载足够高,可能值得将 SQS 作为您系统的一部分进行研究.

于 2008-10-15T14:03:42.473 回答
12

如果您愿意使用单独的软件,我建议您使用 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/

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

于 2012-11-01T05:11:22.877 回答
10
// 每个实际事件一个条目
事件{
  id、时间戳、类型、数据
}

// 每个事件一个条目,每个包含该事件的提要
events_feeds {
  event_id, feed_id
}

创建事件后,确定它出现在哪些提要中并将这些提要添加到 events_feeds。要获取提要,请从 events_feeds 中选择,加入事件,按时间戳排序。然后可以对该查询的结果进行过滤和聚合。使用此模型,您无需额外工作即可在创建后更改事件属性。

于 2008-10-15T20:43:59.873 回答
10

我昨天开始实施这样的系统,这就是我必须...

我创建了一个StreamEvent类,其中包含属性IdActorIdTypeIdDateObjectId和一个包含其他详细信息键/值对的哈希表。这在数据库中由StreamEvent表(IdActorIdTypeIdDateObjectId)和StreamEventDetails表(StreamEventIdDetailKeyDetailValue)表示。

ActorId 、TypeIdObjectId允许捕获(并稍后查询)一个 Subject-Verb-Object 事件每个操作都可能导致创建多个 StreamEvent 实例。

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

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

StreamEvent 的每个子类都有一个 Render( context As StreamContext ) 方法,该方法根据传递的StreamContext类将事件输出到屏幕。StreamContext 类允许根据视图的上下文设置选项。例如,如果您查看 Facebook,主页上的新闻提要列出了参与每个操作的每个人的全名(以及指向他们个人资料的链接),而查看朋友的提要,您只会看到他们的名字(但其他演员的全名) .

我还没有实现聚合提要(Facebook 主页),但我想我会创建一个AggregateFeed表,其中包含字段UserIdStreamEventId,它是基于某种“嗯,你可能会发现这个有趣的”算法填充的。

任何评论将不胜感激。

于 2008-12-09T11:08:06.860 回答
8

如果你决定要在 Rails 中实现,也许你会发现以下插件很有用:

活动流:http: //github.com/face/activity_streams/tree/master

如果不出意外,您将看到一个实现,包括数据模型以及为推送和拉取活动提供的 API。

于 2008-12-03T10:59:03.997 回答
6

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

如上所述,随着站点的增长,它可能会面临可扩展性问题。就个人而言,我现在并不担心扩展问题。我稍后会担心这个。

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

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

这种方法的好处是,您只需在请求特定提要时读取单个数据库表,而不是抓取一系列表。此外,它允许您维护有限的活动列表,因为您可以在更新列表时弹出最旧的活动项。

希望这可以帮助!:)

于 2008-11-19T17:44:50.077 回答
5

关于这样的活动流有两个 railscasts:

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

于 2013-04-18T15:54:27.237 回答
3

我认为Plurk 的方法很有趣:它们以一种看起来很像 Google Finance 的股票图表的格式提供您的整个时间线。

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

于 2008-10-14T18:15:23.793 回答
2

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

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"}
于 2009-11-18T02:50:48.373 回答
2

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

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

如果您想自己托管引擎或需要专门的功能,核心代码实际上是开源的,用于非商业目的,欢迎您查看。

于 2013-06-19T03:20:56.980 回答