首先咱们来看一下市场上关于消息的实现是怎么样的。javascript
简书的消息系统主要分了两种java
简信
简信的性质其实跟私信是同样的,是用户发送给用户的一则消息,有具体的信息内容。ios
提醒
而提醒,则是系统发送的一则消息,其文案格式是固定的,而且对特殊对象通常拥有超连接。json
知乎跟简书同样,主要分了两种:数组
私信
跟简书同样,使用户发送给用户的一则消息,也能够是管理员发送给用户的消息。post
消息
知乎的消息比简书的提醒有过之而无不及,知乎会对多条类似的消息进行聚会,以达到减轻用户阅读压力的体验。单元测试
经过两种产品的简单分析,得出他们的消息有两种分类,在这基础上,咱们再加上一种:公告。
公告的主要性质是系统发送一则含有具体内容的消息,站内全部用户都能读取到这条消息。
因此,消息有三种分类:测试
咱们从简书取一组提醒样本:网站
分析句子结构,提醒的内容无非就是ui
「谁对同样属于谁的事物作了什么操做」
「someone do something in someone's something」
someone = 提醒的触发者,或者发送者,标记为sender
do something = 提醒的动做,评论、喜欢、关注都属于一个动做,标记为action
something = 提醒的动做做用对象,这就具体到是哪一篇文章,标记为target
someone's = 提醒的动做做用对象的全部者,标记为targetOwner
这就清楚了,sender和targetOwner就是网站的用户,而target是具体到哪一篇文章,若是提醒的对象不只仅局限于文章,还有其余的话,就须要增长一项targetType,来标记目标是文章仍是其余的什么。而action,则是固定的,整个网站会触发提醒的动做可能就只有那几样:评论、喜欢、关注.....(或者其余业务须要提醒的动做)
以知乎为例
推的比较常见,须要针对某一个问题维护着一张关注者的列表,每当触发这个问题推送的条件时(例若有人回答问题),就把这个通知发送给每一个关注者。
拉的相对麻烦一点,就是推的反向,例如每一个用户都有一张关注问题的列表,每当用户上线的时候,对每一个问题进行轮询,当问题的事件列表出现了比我本来时间戳大的信息就进行拉取。
而咱们则根据消息的不一样分类采用不一样的获取方式:
通告和提醒,适合使用拉取的方式,消息产生以后,会存在消息表中,用户在某一特定的时间根据本身关注问题的表进行消息的拉取,而后添加到本身的消息队列中,
信息,适合使用推的方式,在发送者创建一条信息以后,同时指定接收者,把消息添加到接收者的消息队列中。
根据提醒使用拉取的方式,须要维护一个关注某一事物的列表。
这种行为,咱们称之为:「订阅」Subscribe
一则订阅有如下三个核心属性:
好比我发布了一篇文章,那么我会订阅文章《XXX》的评论动做,因此文章《XXX》每被人评论了,就须要发送一则提醒告知我。
订阅的规则还能够扩展
我喜欢了一篇文章,和我发布了一篇文章,订阅的动做可能不同。
喜欢了一篇文章,我但愿我订阅这篇文章更新、评论的动做。
而发布了一篇文章,我但愿我只是订阅这篇文章的评论动做。
这时候就须要多一个参数:subscribReason
不一样的subscribReason,对应着一个动做数组,
subscribReason = 喜欢,对应着 actions = [更新,评论]
subscribReason = 发布,对应着 actions = [评论]
订阅的规则还还能够扩展
用户可能会有一个本身的订阅设置,好比对于全部的喜欢的动做,我都不但愿接收。
好比Knewone的提醒设置
因此咱们须要再维护一个表:SubscriptionConfig,来存放用户的提醒设置。
而且,当用户没有提醒设置的时候,可使用系统提供的一套默认设置:defaultSubscriptionConfig
若是我发布了一篇文章《XXX》,在我不在线的时候,被评论了10遍,当我一上线的时候,应该是收到十条信息相似于:「谁谁谁评论了你的文章《XXX》」?
仍是应该收到一条信息:「甲、乙、丙、丁...评论了你的文章《XXX》」?
知乎在聚合上作的很优秀,要知道他们要实现这个仍是挺有技术的:
知乎的消息机制,在技术上如何设计与规划?
网站的消息(通知)系统通常是如何实现的?
关于这部分功能,咱们尚未具体的实现方法,暂时也没法讲得更加详细。⊙﹏⊙
经过上面的分析,大概知道作这个消息系统,须要哪些实体类:
说了这么多,整理一下整个消息流程的一些行为:
id : {type: 'integer', primaryKey: true}, // 主键 content : {type: 'text'}, // 消息的内容 type : {type: 'integer', required: true, enum: [1, 2, 3]}, // 消息的类型,1: 公告 Announce,2: 提醒 Remind,3:信息 Message target : {type: 'integer'}, // 目标的ID targetType : {type: 'string'}, // 目标的类型 action : {type: 'string'}, // 提醒信息的动做类型 sender : {type: 'integer'}, // 发送者的ID createdAt : {type: 'datetime', required: true}
Save Remind
消息表,咱们须要target
、targetType
字段,来记录该条提醒所关联的对象。而action
字段,则记录该条提醒所关联的动做。
好比消息:「小明喜欢了文章」
则:
target = 123, // 文章ID targetType = 'post', // 指明target所属类型是文章 sender = 123456 // 小明ID
Save Announce and Message
固然,Notify还支持存储公告和信息。它们会用到content
字段,而不会用到target
、targetType
、action
字段。
id : {type: 'integer', primaryKey: true}, // 主键 isRead : {type: 'boolean', required: true}, user : {type: 'integer', required: true}, // 用户消息所属者 notify : {type: 'integer', required: true} // 关联的Notify createdAt : {type: 'datetime', required: true}
咱们用UserNotify来存储用户的消息队列,它关联一则提醒(Notify)的具体内容。
UserNotify的建立,主要经过两个途径:
target : {type: 'integer', required: true}, // 目标的ID targetType : {type: 'string', required: true}, // 目标的类型 action : {type: 'string'}, // 订阅动做,如: comment/like/post/update etc. user : {type: 'integer'}, createdAt : {type: 'datetime', required: true}
订阅,是从Notify表拉取消息到UserNotify的前提,用户首先订阅了某一个目标的某一个动做,在此以后产生这个目标的这个动做的消息,才会被通知到该用户。
如:「小明关注了产品A的评论」,数据表现为:
target: 123, // 产品A的ID targetType: 'product', action: 'comment', user: 123 // 小明的ID
这样,产品A下产生的每一条评论,都会产生通知给小明了。
action: {type: 'json', required: true}, // 用户的设置 user: {type: 'integer'}
不一样用户可能会有不同的订阅习惯,在这个表中,用户能够统一针对某种动做进行是否订阅的设置。而默认是使用系统提供的默认配置:
defaultSubscriptionConfig: {
'comment' : true, // 评论 'like' : true, // 喜欢 }
在这套模型中,
targetType
、action
是能够根据需求来扩展的,例如咱们还能够增长多几个动做的提醒:hate
被踩、update
被更新....诸如此类。
// 提醒关联的目标类型 targetType: { PRODUCT : 'product', // 产品 POST : 'post' // 文章 }, // 提醒关联的动做 action: { COMMENT : 'comment', // 评论 LIKE : 'like', // 喜欢 }, // 订阅缘由对应订阅事件 reasonAction: { 'create_product' : ['comment', 'like'] 'like_product' : ['comment'], 'like_post' : ['comment'], }, // 默认订阅配置 defaultSubscriptionConfig: { 'comment' : true, // 评论 'like' : true, // 喜欢 }
createAnnounce(content, sender)
createRemind(target, targetType, action, sender, content)
createMessage(content, sender, receiver)
pullAnnounce(user)
lastTime
lastTime
做为过滤条件,查询Notify的公告信息pullRemind(user)
target
、targetType
、action
、createdAt
去查询Notify表,获取订阅的Notify记录。(注意订阅时间必须早于提醒建立时间)subscribe(user, target, targetType, reason)
actions
cancelSubscription(user, target ,targetType)
user
、target
、targetType
对应的一则或多则记录getSubscriptionConfig(userID)
updateSubscriptionConfig(userID)
getUserNotify(userID)
read(user, notifyIDs)
咱们能够在产品建立以后,调用NotifyService.subscribe
方法,
而后在产品被评论以后调用NotifyService.createRemind
方法,
再就是用户登陆系统或者其余的某一个时刻调用NotifyService.pullRemind
方法,
最后在用户查询消息队列的时候调用NotifyService.getUserNotify
方法。
在管理员发送了一则公告的时候,调用NotifyService.createAnnounce
方法,
而后在用户登陆系统或者其余的某一个时刻调用NotifyService.pullAnnounce
方法,
最后在用户查询消息队列的时候调用NotifyService.getUserNotify
方法。
信息的建立,只须要直接调用NotifyService.createMessage
方法就能够了,在下一次用户查询消息队列的时候,就会查询这条信息。