我很想听听你对实现社交活动流的最佳方式(Facebook就是最著名的例子)的看法。涉及的问题/挑战有:
不同类型的活动(张贴,评论..)
不同类型的对象(帖子,评论,照片..)
1-n个不同角色的用户(“用户x回复了用户y对用户Z帖子的评论”)
同一活动项的不同视图(“您评论了..”vs。“你的朋友x评论”vs。"用户x评论说.."3个“评论”活动的表示)
. .还有更多,特别是如果你把它提高到一个高度复杂的水平,比如,把几个活动项目合并成一个(“用户x, y和z评论了那张照片”)。
任何关于模式、论文等关于最灵活、有效和强大的方法来实现这样一个系统、数据模型等的想法或建议都将受到欢迎。
尽管大多数问题与平台无关,但我最终有可能在Ruby on Rails上实现这样一个系统
事件流最大的问题是可见性和性能;您需要将显示的事件限制为只显示该特定用户感兴趣的事件,并且需要保持整理和识别这些事件所需的时间。我建立了一个小型的社交网络;我发现,在小范围内,在数据库中保留“事件”表是可行的,但在中等负载下就会出现性能问题。
对于较大的消息流和用户,最好使用消息传递系统,将事件作为消息发送到单个配置文件。这意味着您不能很容易地订阅人们的事件流,也不能很容易地查看以前的事件,但是当您需要为特定用户呈现流时,您只是呈现了一小组消息。
I believe this was Twitter's original design flaw- I remember reading that they were hitting the database to pull in and filter their events. This had everything to do with architecture and nothing to do with Rails, which (unfortunately) gave birth to the "ruby doesn't scale" meme. I recently saw a presentation where the developer used Amazon's Simple Queue Service as their messaging backend for a twitter-like application that would have far higher scaling capabilities- it may be worth looking into SQS as part of your system, if your loads are high enough.
我创建了这样一个系统,我采取了这样的方法:
数据库表中包含以下列:id、userId、类型、数据、时间。
userId是生成活动的用户
type是活动的类型(即写博客,添加照片,评论用户照片)
Data是一个带有活动元数据的序列化对象,您可以在其中放入任何您想要的内容
这限制了用户、时间和活动类型的搜索/查找,但在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
I have been looking into better solutions than the denormalized table I mentioned above. Another way I have found of accomplishing this is to condense all the content that would be in a given activity stream into a single row. It could be stored in XML, JSON, or some serialized format that could be read by your application. The update process would be simple too. Upon activity, place the new activity into a queue (perhaps using Amazon SQS or something else) and then continually poll the queue for the next item. Grab that item, parse it, and place its contents in the appropriate feed object stored in the database.
这种方法的优点是,每当请求特定提要时,您只需要读取一个数据库表,而不是获取一系列表。此外,它允许您维护一个有限的活动列表,因为每当您更新列表时,您可能会弹出最古老的活动项。
希望这能有所帮助!:)
我昨天开始执行一个这样的系统,这就是我要做的…
我创建了一个带有属性Id、ActorId、TypeId、Date、ObjectId和附加细节键/值对哈希表的StreamEvent类。这在数据库中由一个StreamEvent表(Id, actid, TypeId, Date, ObjectId)和一个StreamEventDetails表(StreamEventId, DetailKey, DetailValue)表示。
ActorId、TypeId和ObjectId允许捕获一个Subject-Verb-Object事件(以及稍后查询)。每个操作都可能导致创建多个StreamEvent实例。
然后,我为StreamEvent的每种类型的事件创建了一个子类,例如LoginEvent, PictureCommentEvent。这些子类中的每一个都有更多上下文特定的属性,如PictureId, ThumbNail, CommenText等(事件所需的任何属性),这些属性实际上存储为hashtable/StreamEventDetail表中的键/值对。
当从数据库中提取这些事件时,我使用一个工厂方法(基于TypeId)来创建正确的StreamEvent类。
StreamEvent的每个子类都有一个Render(context As StreamContext)方法,该方法根据传递的StreamContext类将事件输出到屏幕。StreamContext类允许基于视图的上下文设置选项。以Facebook为例,主页上的信息流会列出参与每个行动的每个人的全名(以及他们个人资料的链接),而查看朋友的信息流,你只能看到他们的名字(但其他参与者的全名)。
我还没有实现一个聚合提要(Facebook家),但我想我会创建一个AggregateFeed表,其中有字段UserId, 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"}
如果您愿意使用单独的软件,我建议使用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个流)。
After implementing activity streams to enable social feeds, microblogging, and collaboration features in several applications, I realized that the base functionality is quite common and could be turned into an external service that you utilize via an API. If you are building the stream into a production application and do not have unique or deeply complex needs, utilizing a proven service may be the best way to go. I would definitely recommend this for production applications over rolling your own simple solution on top of a relational database.
我的公司collaboration (http://www.collabinate.com)就是从这种实现中发展出来的,我们在图形数据库上实现了一个可伸缩的高性能活动流引擎来实现它。实际上,我们使用了Graphity算法的变体(改编自@RenePickhardt的早期工作,他也在这里提供了答案)来构建引擎。
如果您希望自己托管引擎或需要特殊功能,其核心代码实际上是非商业用途的开源代码,因此欢迎您查看。
我们开放了我们的方法:
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-feeds.html
本教程将帮助您使用Redis设置像Pinterest的提要这样的系统。这很容易上手。
要了解更多关于feed设计的知识,我强烈建议阅读一些我们基于Feedly的文章:
雅虎研究报告
Twitter 2013 Redis的基础上,与后退
Cassandra在Instagram
Etsy feed缩放
Facebook的历史
Django项目,具有良好的命名约定。(仅限数据库)
http://activitystrea.ms/specs/atom/1.0/(行动者,动词,对象,目标)
Quora上关于最佳实践的帖子
Quora扩展了社交网络
Redis红宝石示例
FriendFeed的方法
Thoonk设置
Twitter的方法
虽然Stream Framework是基于Python的,但从Ruby应用程序中使用它并不太难。你可以简单地将它作为服务运行,并在它前面插入一个小的http API。我们正在考虑添加一个API来从其他语言访问Feedly。不过现在你得扮演你自己的角色。