通知功能、消息系统设计与实现(上下片)《版本1》 |
您所在的位置:网站首页 › braun剃须刀配件3612 › 通知功能、消息系统设计与实现(上下片)《版本1》 |
上篇主要讲的是一些概念,搞清楚我们要做的这个消息系统的主要内容。 下篇主要讲具体的实现,会包括架构设计,数据库设计,业务流程详细的实现等。 整个系统的设计与实现,并非我一人之力就可以完成的。这其中是同事们大家一起讨论与商讨的结果,而我只是把它细化,呈现出来。 产品分析首先我们来看一下市场上关于消息的实现是怎么样的。 → 简书简书的消息系统主要分了两种 简信提醒简信 简信的性质其实跟私信是一样的,是用户发送给用户的一则消息,有具体的信息内容。 简书简信 提醒 而提醒,则是系统发送的一则消息,其文案格式是固定的,并且对特殊对象一般拥有超链接。 简书提醒 知乎 知乎跟简书一样,主要分了两种: 私信消息私信 跟简书一样,使用户发送给用户的一则消息,也可以是管理员发送给用户的消息。 知乎私信
消息 知乎的消息比简书的提醒有过之而无不及,知乎会对多条相似的消息进行聚会,以达到减轻用户阅读压力的体验。 知乎消息 消息的三种分类 通过两种产品的简单分析,得出他们的消息有两种分类,在这基础上,我们再加上一种:公告。 公告的主要性质是系统发送一则含有具体内容的消息,站内所有用户都能读取到这条消息。 所以,消息有三种分类: 公告 Announce提醒 Remind私信 Message 提醒的语言分析我们从简书取一组提醒样本: 3dbe1bd90774 关注了你magicdawn 喜欢了你的文章 《单点登录的三种实现方式》无良程序 喜欢了你的文章 《基于RESTful API 怎么设计用户权限控制?》alexcc4 喜欢了你的文章 《在Nodejs中贯彻单元测试》你在《基于RESTful API 怎么设计用户权限控制?》中收到一条 cnlinjie 的评论你的文章《Session原理》已被加入专题 《ios开发》分析句子结构,提醒的内容无非就是 「谁对一样属于谁的事物做了什么操作」 「someone do something in someone's something」 someone = 提醒的触发者,或者发送者,标记为sender do something = 提醒的动作,评论、喜欢、关注都属于一个动作,标记为action something = 提醒的动作作用对象,这就具体到是哪一篇文章,标记为target someone's = 提醒的动作作用对象的所有者,标记为targetOwner 这就清楚了,sender和targetOwner就是网站的用户,而target是具体到哪一篇文章,如果提醒的对象不仅仅局限于文章,还有其他的话,就需要增加一项targetType,来标记目标是文章还是其他的什么。而action,则是固定的,整个网站会触发提醒的动作可能就只有那几样:评论、喜欢、关注.....(或者其他业务需要提醒的动作) 消息的两种获取方式 推 Push拉 Pull以知乎为例 推的比较常见,需要针对某一个问题维护着一张关注者的列表,每当触发这个问题推送的条件时(例如有人回答问题),就把这个通知发送给每个关注者。 拉的相对麻烦一点,就是推的反向,例如每个用户都有一张关注问题的列表,每当用户上线的时候,对每个问题进行轮询,当问题的事件列表出现了比我原本时间戳大的信息就进行拉取。 而我们则根据消息的不同分类采用不同的获取方式: 通告和提醒,适合使用拉取的方式,消息产生之后,会存在消息表中,用户在某一特定的时间根据自己关注问题的表进行消息的拉取,然后添加到自己的消息队列中, 信息,适合使用推的方式,在发送者建立一条信息之后,同时指定接收者,把消息添加到接收者的消息队列中。 订阅根据提醒使用拉取的方式,需要维护一个关注某一事物的列表。 这种行为,我们称之为:**「订阅」Subscribe ** 一则订阅有以下三个核心属性: 订阅的目标 target订阅的目标类型 targetType订阅的动作 action比如我发布了一篇文章,那么我会订阅文章《XXX》的评论动作,所以文章《XXX》每被人评论了,就需要发送一则提醒告知我。 订阅的规则还可以扩展 我喜欢了一篇文章,和我发布了一篇文章,订阅的动作可能不一样。 喜欢了一篇文章,我希望我订阅这篇文章更新、评论的动作。 而发布了一篇文章,我希望我只是订阅这篇文章的评论动作。 这时候就需要多一个参数:subscribReason 不同的subscribReason,对应着一个动作数组, subscribReason = 喜欢,对应着 actions = [更新,评论] subscribReason = 发布,对应着 actions = [评论] 订阅的规则还还可以扩展 用户可能会有一个自己的订阅设置,比如对于所有的喜欢的动作,我都不希望接收。 比如Knewone的提醒设置 Knewone提醒设置
所以我们需要再维护一个表:SubscriptionConfig,来存放用户的提醒设置。 并且,当用户没有提醒设置的时候,可以使用系统提供的一套默认设置:defaultSubscriptionConfig 聚合如果我发布了一篇文章《XXX》,在我不在线的时候,被评论了10遍,当我一上线的时候,应该是收到十条信息类似于:「谁谁谁评论了你的文章《XXX》」? 还是应该收到一条信息:「甲、乙、丙、丁...评论了你的文章《XXX》」? 知乎在聚合上做的很优秀,要知道他们要实现这个还是挺有技术的:知乎的消息机制,在技术上如何设计与规划?网站的消息(通知)系统一般是如何实现的? 关于这部分功能,我们还没有具体的实现方法,暂时也无法讲得更加详细。⊙﹏⊙ 五个实体通过上面的分析,大概知道做这个消息系统,需要哪些实体类: 用户消息队列 UserNotify用户 User订阅 Subscription订阅设置 SubscriptionConfig消息 Notify 通告 Announce提醒 Remind信息 Message 行为分解说了这么多,整理一下整个消息流程的一些行为: 系统或者管理员,创建消息 createNotify (make announce | remind | message)用户,订阅消息,取消订阅 subscribe, cancelSubscription用户管理订阅设置 getSubscriptionConfig, updateSubscriptionConfig用户,拉取消息 pullNotify (pull announce | remind | message | all)用户,查询消息队列 getUserNotify(get announce | remind | message | all)用户阅读消息 read在本文的「下篇」我们来探讨一下:模型怎么做、数据库怎么设计、代码结构怎么来、一些逻辑上的时序图应该是怎么样的。
下篇------------------------ 模型设计 Notify 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 // 小明IDSave Announce and Message 当然,Notify还支持存储公告和信息。它们会用到content字段,而不会用到target、targetType、action字段。 UserNotify 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的创建,主要通过两个途径: 遍历订阅(Subscription)表拉取公告(Announce)和提醒(Remind)的时候创建新建信息(Message)之后,立刻创建。 Subscription 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下产生的每一条评论,都会产生通知给小明了。 SubscriptionConfig action: {type: 'json', required: true}, // 用户的设置 user: {type: 'integer'}不同用户可能会有不一样的订阅习惯,在这个表中,用户可以统一针对某种动作进行是否订阅的设置。而默认是使用系统提供的默认配置: defaultSubscriptionConfig: { 'comment' : true, // 评论 'like' : true, // 喜欢 }在这套模型中,targetType、action是可以根据需求来扩展的,例如我们还可以增加多几个动作的提醒:hate被踩、update被更新....诸如此类。 配置文件 NotifyConfig // 提醒关联的目标类型 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, // 喜欢 } 服务层 NotifyServiceNotifyService拥有以下方法: createAnnounce(content, sender)createRemind(target, targetType, action, sender, content)createMessage(content, sender, receiver)pullAnnounce(user)pullRemind(user)subscribe(user, target, targetType, reason)cancelSubscription(user, target ,targetType)getSubscriptionConfig(userID)updateSubscriptionConfig(userID)getUserNotify(userID)read(user, notifyIDs)各方法的处理逻辑如下: createAnnounce(content, sender) 往Notify表中插入一条公告记录createRemind(target, targetType, action, sender, content) 往Notify表中插入一条提醒记录createMessage(content, sender, receiver) 往Notify表中插入一条信息记录往UserNotify表中插入一条记录,并关联新建的NotifypullAnnounce(user) 从UserNotify中获取最近的一条公告信息的创建时间: lastTime用lastTime作为过滤条件,查询Notify的公告信息新建UserNotify并关联查询出来的公告信息pullRemind(user) 查询用户的订阅表,得到用户的一系列订阅记录通过每一条的订阅记录的target、targetType、action、createdAt去查询Notify表,获取订阅的Notify记录。(注意订阅时间必须早于提醒创建时间)查询用户的配置文件SubscriptionConfig,如果没有则使用默认的配置DefaultSubscriptionConfig使用订阅配置,过滤查询出来的Notify使用过滤好的Notify作为关联新建UserNotifysubscribe(user, target, targetType, reason) 通过reason,查询NotifyConfig,获取对应的动作组:actions遍历动作组,每一个动作新建一则Subscription记录cancelSubscription(user, target ,targetType) 删除user、target、targetType对应的一则或多则记录getSubscriptionConfig(userID) 查询SubscriptionConfig表,获取用户的订阅配置updateSubscriptionConfig(userID) 更新用户的SubscriptionConfig记录getUserNotify(userID) 获取用户的消息列表read(user, notifyIDs) 更新指定的notify,把isRead属性设置为true 时序图 提醒的订阅、创建、拉取提醒的订阅、创建、拉取 我们可以在产品创建之后,调用NotifyService.subscribe方法, 然后在产品被评论之后调用NotifyService.createRemind方法, 再就是用户登录系统或者其他的某一个时刻调用NotifyService.pullRemind方法, 最后在用户查询消息队列的时候调用NotifyService.getUserNotify方法。 公告的创建、拉取
公告的创建、拉取 在管理员发送了一则公告的时候,调用NotifyService.createAnnounce方法, 然后在用户登录系统或者其他的某一个时刻调用NotifyService.pullAnnounce方法, 最后在用户查询消息队列的时候调用NotifyService.getUserNotify方法。 信息的创建
信息的创建 信息的创建,只需要直接调用NotifyService.createMessage方法就可以了, 在下一次用户查询消息队列的时候,就会查询这条信息。
如果本文对您有用 请不要吝啬你们的Follow与Start 这会大大支持继续创作 「Github」 感谢以下大佬: MZMonster :@MZMonster JC_Huang :@JerryC8080
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |