建立通知系统


170

我正在开始为我们的页面(社交游戏类型)构建Facebook样式通知系统,现在我正在研究什么是设计此类系统的最佳方法。我对如何将通知推送给用户或诸如此类的东西不感兴趣(到目前为止)。我正在研究如何在服务器上构建系统(如何存储通知,将通知存储在哪里,如何获取通知等)。

所以...我们有一些要求:

  • 在高峰时段,我们大约有1000个并发登录用户(还有更多的来宾,但他们在这里无关紧要,因为他们不会收到通知),这些事件会生成许多事件
  • 会有不同类型的通知(用户A已将您添加为朋友,用户B已对您的个人资料发表评论,用户C喜欢您的图像,用户D在游戏X上击败了您,...)
  • 大多数事件将为1个用户生成1条通知(用户X喜欢您的图片),但是在某些情况下,一个事件将生成许多通知(例如,用户Y的生日)
  • 通知应分组在一起;例如,如果四个不同的用户喜欢某个图像,则该图像的所有者应收到一个通知,指出四个用户喜欢该图像,而不是四个单独的通知(就像FB一样)

好的,所以我当时想的是应该创建某种队列,以便在事件发生时存储事件。然后,我将有一个后台作业(gearman?),它将查看该队列并根据这些事件生成通知。然后,此作业会将每个用户的通知存储在数据库中(因此,如果一个事件影响10个用户,则将有10个单独的通知)。然后,当用户打开一个包含通知列表的页面时,我会为他阅读所有这些通知(我们打算将其限制为100个最新通知)并将它们组合在一起,最后显示它们。

我担心这种方法的事情:

  • 复杂如地狱:)
  • 是数据库的最佳存储(我们正在使用MySQL)还是我应该使用其他存储(redis似乎也很合适)
  • 我应该将什么存储为通知?用户ID,发起事件的用户ID,事件的类型(以便我可以将其分组并显示适当的文本),但是我有点不知道如何存储通知的实际数据(例如,图片的URL和标题)很喜欢)。我应该在生成通知时只是“烘焙”该信息,还是应该存储受影响的记录的ID(图像,配置文件...),并在显示通知时将其从数据库中拉出。
  • 即使在显示通知页面时我必须即时处理100条通知,此处的性能也应该可以
  • 每个请求上可能存在性能问题,因为我必须向用户显示未读通知的数量(这可能是个问题,因为我会将通知分组在一起)。但是,如果我在后台而不是即时生成通知视图(将通知分组的位置),则可以避免这种情况

那么,您如何看待我提出的解决方案和担忧?如果您认为我应该在这里提及其他相关问题,请发表评论。

哦,我们在页面上使用的是PHP,但我认为这并不是一个大因素。


一个人的工作花费了您构建此通知系统的时间。我只想估计一下时间即可。
Shaharyar 2015年

@Shaharyar我认为这取决于通知系统的复杂性。
tyan

我使用了与MySQL相同的系统来构建基于优先级的通知系统。好消息是它可以扩展到几千个用户,如果超过这个数量,它就会爆炸,特别是在Android和GCM上。我想知道MySQL的替代品,例如redis,rabbitMQ,Kafka,它们自然地具有消息队列和某种功能。
Ankit Marothi '17年

Answers:


168

通知是有关某人(演员)更改(动词=添加,请求..)并报告给用户(主题)的某事(对象=事件,友谊..)。这是规范化的数据结构(尽管我使用过MongoDB)。您需要通知某些用户有关更改。因此,这是每位用户的通知。这意味着,如果有100位用户参与,您将生成100条通知。

╔═════════════╗      ╔═══════════════════╗      ╔════════════════════╗
║notification ║      ║notification_object║      ║notification_change ║
╟─────────────╢      ╟───────────────────╢      ╟────────────────────╢
║ID           ║—1:n—→║ID                 ║—1:n—→║ID                  ║
║userID       ║      ║notificationID     ║      ║notificationObjectID║
╚═════════════╝      ║object             ║      ║verb                ║
                     ╚═══════════════════╝      ║actor               ║
                                                ╚════════════════════╝

(在您认为合适的地方添加时间字段)

基本上,这是为了对每个对象的更改进行分组,因此您可以说“您有3个朋友请求”。每个演员的分组非常有用,因此您可以说“用户James Bond在床上进行了更改”。这也使您能够根据需要翻译和计数通知。

但是,由于object只是一个ID,因此您需要通过单独的调用获取有关所需对象的所有额外信息,除非对象实际上发生了更改并且您想显示该历史记录(例如,“用户将事件标题更改为... ”)

由于通知对于站点上的用户而言是接近实时的,因此我将它们与nodejs + websockets客户端绑定在一起,并使用php将更新推送到所有侦听器的nod​​ejs,以添加更改。


1
notification_object.object标识更改类型,例如字符串“ friendship”,我所谈论的对更改对象及其额外数据的实际引用位于notification_change.notificationObjectID中
Artjom Kurapov 2013年

2
这可能是一个愚蠢的问题,但是通过这种设置,一旦用户看到或执行了通知,您将如何处理?您是将其从数据库中删除还是仅使用日期来查看自创建通知以来用户是否已登录?
Jeffery Mills 2014年

4
我知道这个话题已经很老了,但是我对第一个表有些疑惑,这个表的目的是什么?与将userID放入notification_object表相比,将其作为单独的表有什么好处?换句话说,何时将在通知中创建新条目,何时将仅添加对象并更改为具有此结构的现有通知?
Bas Goossen 2014年

3
@JefferyMills你可以有一个领域的地位就像is_notification_readnotification表中,并适当地标记它,如果它是unreadreaddeleted
凯文

2
我也一直在努力了解这个解决方案的某些方面,并提出了关于它的另外一个问题:dba.stackexchange.com/questions/99401/...
user45623

27

这确实是一个抽象的问题,所以我想我们将不得不讨论该问题,而不是指出您应该或不应该做的事情。

这是我对您的疑虑的看法:

  • 是的,通知系统很复杂,但并非如此。您可以采用多种不同的方法来建模和实现这样的系统,并且它们的复杂性可能从中等到高度。

  • 从个人的角度来看,我总是尝试使资料驱动数据库。为什么?因为我可以保证完全控制所有发生的事情-但这仅仅是我,您无需数据库驱动的方法就可以控制。相信我,你会想要控制这种情况;

  • 让我为您举例说明一个实际案例,以便您可以从某个地方入手。在过去的一年中,我已经在某种社交网络中建模并实现了一个通知系统(当然不像Facebook)。我过去在那里存储通知的方式?我有一个notifications表,其中保存着generator_user_id(正在生成通知的用户的ID),target_user_id(很明显,不是吗?),notification_type_id(引用了具有通知类型的另一个表)和所有我们需要用表(时间戳,标志等)填充表中的必要内容。我的notification_types表过去与一个notification_templates表有关系,该表存储了每种通知类型的特定模板。例如,我有一个POST_REPLY类型,它有一个类似like的模板{USER} HAS REPLIED ONE OF YOUR #POSTS。从那里,我只是对待{}作为变量和#作为参考链接;

  • 是的,性能应该并且必须可以。当您想到通知时,就会想到服务器从头到脚推。无论您是要使用ajax请求还是执行其他操作,您都必须担心性能。但是我认为这是第二次关注。

当然,我设计的模型不是唯一可以遵循的模型,也不是最好的模型。我希望我的回答至少可以帮助您朝正确的方向发展。


为什么我无法控制其他数据存储?
JanHančič12年

好吧,我没有那么说。我的意思是,我只能用数据库驱动的方法来保证数据控制。就是我 我要改一下。
丹尼尔·里贝罗

@DanielRibeiro通知模板中的占位符({...})需要针对不同类型的通知替换数据库中不同表集的占位符数据。例如,一个模板是“ {用户}喜欢您的照片。”,另一个模板是“您的{页面名称}有新的赞。”。{PageName}和{user}等占位符将从不同的数据库表中映射,因此应采用什么模式来动态获取占位符值。
Ashish Shukla

DanielRibeiro如何按照@Ashish Shukla的要求替换占位符,
Shantaram

@AshishShukla您使用或替换了占位符,如何?
Shantaram Tupe

8
╔════════════════════╗
║notification        ║
╟────────────────────╢
║Username            ║
║Object              ║
║verb                ║
║actor               ║
║isRead              ║
╚════════════════════╝

这似乎是一个很好的答案,而不是有2个集合。您可以通过用户名,对象和isRead查询以获取新事件(例如3个待处理的好友请求,4个问题等)。

让我知道此架构是否存在问题。


3
最重要的答案是使用规范化的数据结构,这意味着表中没有冗余。你的答案能做到吗?
亚伦·霍尔

4

我个人不太了解所接受答案的图表,因此我将根据我从所接受答案和其他页面中学到的内容来附加数据库图表。

在此处输入图片说明

改进受到好评。


似乎message_template将在NotificationType表中。似乎main_url也会出现在通知表中,那么您可以消除Notification_Message表。您能解释一下拥有NotificationMessage表的原因吗?
杰夫·瑞安
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.