消息通知系统模型设计

huangcong huangcong     2022-12-05     785

关键词:

本篇主要明确消息通知系统的概念和具体实现,包括数据库设计、技术方案、逻辑关系分析等。消息通知系统是一个比较复杂的系统,这里主要分析站内消息如何设计和实现。

我们常见的消息推送渠道有以下几种:
  • 设备推送
  • 站内推送
  • 短信推送
  • 邮箱推送
我们常见的站内通知有以下几种类别:
  • 公告 Announcement
  • 提醒 Remind

    • 资源订阅提醒「我关注的资源有更新、评论等事件时通知我」
    • 资源发布提醒「我发布的资源有评论、收藏等事件时通知我」
    • 系统提醒「平台会根据一些算法、规则等可能会对你的资源做一些事情,这时你会收到系统通知」
  • 私信 Mailbox

以上三种消息有各自特点,实现也各不相同,其中「提醒」类通知是最复杂的,下面会详细讲。

数据模型设计

公告

公告是指平台发送一条含有具体内容的消息,站内所有用户都能收到这条消息。

方案一:【适合活跃用户在5万左右】

公告表「notify_announce」 
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //公告编号;
  2.  
    senderID: type: ‘string‘, required: true //发送者编号,通常为系统管理员;
  3.  
    title: type: ‘string‘, required: true //公告标题;
  4.  
    content: type: ’text‘, required: true //公告内容;
  5.  
    createdAt: type: ‘timestamp‘, required: true //发送时间;

用户公告表「notify_announce_user」 
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //用户公告编号;
  2.  
    announceID: type: ‘integer‘ //公告编号;
  3.  
    recipientID: type: ‘string‘, required: true //接收用户编号;
  4.  
    createdAt:type: ‘timestamp‘, required: true //拉取公告时间;
  5.  
    state: type: ‘integer‘, required: true //状态,已读|未读;
  6.  
    readAt:type: ‘timestamp‘, required: true //阅读时间;

平台发布一则公告之后,当用户登录的时候去拉取站内公告并插入notify_announce_user表,这样那些很久都没登陆的用户就没必要插入了。「首次拉取,根据用户的注册时间;否则根据notify_announce_user.createdAt即上一次拉取的时间节点获取公告」

方案二:【适合活跃用户在百万-千万左右】

和方案一雷同,只是需要把notify_announce_user表进行哈希分表,需事先生成表:notify_announce_<hash(uid)>。

用户公告表「notify_announce_<hash(uid)>」 
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //用户公告编号;
  2.  
    announceID: type: ‘integer‘ //公告编号;
  3.  
    recipientID: type: ‘string‘, required: true //接收用户编号;
  4.  
    createdAt:type: ‘timestamp‘, required: true //拉取公告时间;
  5.  
    state: type: ‘integer‘, required: true //状态,已读|未读;
  6.  
    readAt:type: ‘timestamp‘, required: true //阅读时间;

提醒

提醒是指「我的资源」或「我关注的资源」有新的动态产生时通知我。提醒的内容无非就是: 
「someone do something in someone‘s something」 
「谁对一样属于谁的事物做了什么操作」

常见的提醒消息例子,如: 
XXX 关注了你 - 「这则属于资源发布提醒」 
XXX 喜欢了你的文章 《消息通知系统模型设计》 - 「这则属于资源发布提醒」 
你喜欢的文章《消息通知系统模型设计》有新的评论 - 「这则属于资源订阅提醒」 
你的文章《消息通知系统模型设计》已被加入专题 《系统设计》 - 「这则属于系统提醒」 
小明赞同了你的回答 XXXXXXXXX -「这则属于资源发布提醒」

最后一个例子中包含了消息的生产者(小明),消息记录的行为(赞同),行为的对象(你的回答内容)

分析提醒类消息的句子结构: 
someone = 动作发起者,标记为「sender」 
do something = 对资源的操作,如:评论、喜欢、关注都属于一个动作,标记为「action」 
something = 被作用对象,如:一篇文章,文章的评论等,标记为「object」 
someone‘s = 动作的目标对象或目标资源的所有者,标记为「objectOwner」

总结:sender 和 objectOwner 就是网站的用户,object 就是网站资源,可能是一篇文章,一条文章的评论等等。action 就是动作,可以是赞、评论、收藏、关注、捐款等等。

提醒设置

提醒通常是可以在「设置-通知」里自定义配置的,用户可以选择性地「订阅」接收和不接收某类通知。

呈现在界面上是这样的:

  1.  
    通知设置
  2.  
     
  3.  
    我发布的 publish
  4.  
    文章
  5.  
    被 评论 是/否 通知我
  6.  
    被 收藏 是/否 通知我
  7.  
    被 点赞 是/否 通知我
  8.  
    被 喜欢 是/否 通知我
  9.  
    被 捐款 是/否 通知我
  10.  
     
  11.  
    我订阅的 follow
  12.  
    文章
  13.  
    有 更新 是/否 通知我
  14.  
    被 评论 是/否 通知我

订阅

一般系统默认是订阅了所有通知的。系统在给用户推送消息的时候必须查询通知「订阅」模块,以获取某一事件提醒消息应该推送到哪些用户。
也就是说「事件」和「用户」之间有一个订阅关系。

那么接下来我们分析下「订阅」有哪些关键元素:

比如我发布了一篇文章,那么我会订阅文章《XXX》的评论动作,所以文章《XXX》每被人评论了,就需要发送一则提醒告知我。

分析得出以下关键元素:

  • 订阅者「subscriber」
  • 订阅的对象「object」
  • 订阅的动作「action」
  • 订阅对象和订阅者的关系「objectRelationship」

什么是订阅的目标关系呢?

拿知乎来说,比如我喜欢了一篇文章,我希望我订阅这篇文章的更新、评论动作。那这篇文章和我什么关系?不是所属关系,只是喜欢。

  • objectRelationship = 我发布的,对应着 actions = [评论,收藏]
  • objectRelationship = 我喜欢的,对应着 actions = [更新,评论]

讲了那么多,现在来构建「提醒」的数据结构该吧!

提醒表「notify_remind」 
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //主键;
  2.  
    remindID: type: ‘string‘, required: true //通知提醒编号;
  3.  
    senderID: type: ‘string‘, required: true //操作者的ID,三个0代表是系统发送的;
  4.  
    senderName: type: ‘string’, required: true //操作者用户名;
  5.  
    senderAction: type: ‘string‘, required: true //操作者的动作,如:赞了、评论了、喜欢了、捐款了、收藏了;
  6.  
    objectID: type: ‘string‘, required: true, //目标对象ID;
  7.  
    object: type: ‘string‘, required: false, //目标对象内容或简介,比如:文章标题;
  8.  
    objectType: type: ‘string‘, required: true //被操作对象类型,如:人、文章、活动、视频等;
  9.  
    recipientID: type: ‘string’ //消息接收者;可能是对象的所有者或订阅者;
  10.  
    message: type: ‘text‘, required: true //消息内容,由提醒模版生成,需要提前定义;
  11.  
    createdAt:type: ‘timestamp‘, required: true //创建时间;
  12.  
    status:type: ‘integer‘, required: false //是否阅读,默认未读;
  13.  
    readAt:type: ‘timestamp‘, required: false //阅读时间;

假如:特朗普关注了金正恩,以下字段的值是这样的

  1.  
    senderID = 特朗普的ID
  2.  
    senderName = 特朗普
  3.  
    senderAction = 关注
  4.  
    objectID = 金正恩的ID
  5.  
    object = 金正恩
  6.  
    objectType = 人
  7.  
    recipientID = 金正恩的ID
  8.  
    message = 特朗普关注了金正恩
  9.  
     
  10.  
    这种情况objectID 和 recipientID是一样的。
这里需要特别说下消息模版,模版由「对象」、「动作」和「对象关系」构成唯一性。

通知提醒订阅表「notify_remind_subscribe」 
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //订阅ID;
  2.  
    userID: type: ‘string‘, required: true,//用户ID,对应 notify_remind 中的 recipientID;
  3.  
    objectType: type: ‘string‘, required: true //资源对象类型,如:文章、评论、视频、活动、用户;
  4.  
    action: type: ‘string‘, required: true //资源订阅动作,多个动作逗号分隔如: comment,like,post,update etc.
  5.  
    objectRelationship: type: ‘string‘, required: true //用户与资源的关系,用户发布的published,用户关注的followed;
  6.  
    createdAt:type: ‘timestamp‘, required: true //创建时间;
特别说下「objectRelationship」字段的作用,这个字段用来区分「消息模版」,为什么呢?因为同一个「资源对象」和「动作」会有两类订阅者,一类是该资源的Owner,另一类是该资源的Subscriber,这两类人收到的通知消息内容应该是不一样的。

聚合

假如我在抖音上发布了一个短视频,在我不在线的时候,被评论了1000遍,当我一上线的时候,应该是收到一千条消息,类似于:「* 评论了你的文章《XXX》」? 还是应该收到一条信息:「有1000个人评论了你的文章《XXX》」?

当然是后者更好些,要尽可能少的骚扰用户。

消息推送

是不是感觉有点晕了,还是先上一张消息通知的推送流程图吧: 
技术图片

订阅表一共有两张噢,一张是「通知订阅表」、另一张是用户对资源的「对象订阅表」。 
具体实现就不多讲了,配合这张图,理解上面讲的应该不会有问题了。

私信

通常私信有这么几种需求:

  • 点到点:用户发给用户的站内信,系统发给用户的站内信。「1:1」
  • 点到多:系统发给多个用户的站内信,接收对象较少,而且接收对象无特殊共性。「1:N」
  • 点到面:系统发给用户组的站内信,接收对象同属于某用户组之类的共同属性。「1:N」
  • 点到全部:系统发给全站用户的站内信,接收对象为全部用户,通常为系统通知。「1:N」

这里主要讲「点到点」的站内信。

私信表「notify_mailbox」
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //编号;
  2.  
    dialogueID: type: ‘string‘, required: true //对话编号;
  3.  
    senderID: type: ‘string‘, required: true //发送者编号;
  4.  
    recipientID: type: ‘string‘, required: true //接收者编号;
  5.  
    messageID: type: ‘integer‘, required: true //私信内容ID;
  6.  
    createdAt:type: ‘timestamp‘, required: true //发送时间;
  7.  
    state: type: ‘integer‘, required: true //状态,已读|未读;
  8.  
    readAt:type: ‘timestamp‘, required: true //阅读时间;

Inbox

  1.  
    私信列表
  2.  
    select * from notify_inbox where recipientID="uid" order by createdAt desc
  3.  
     
  4.  
    对话列表
  5.  
    select * from notify_inbox where dialogueID=“XXXXXXXXXXXX” and (recipientID=“uid” or senderID="uid") order by createdAt asc

私信回复时,回复的是dialogueID

Outbox

  1.  
    私信列表
  2.  
    select * from notify_inbox where senderID="uid" order by createdAt desc
  3.  
     
  4.  
    对话列表
  5.  
    select * from notify_inbox where dialogueID=“XXXXXXXXXXXX” and (senderID=“uid” or recipientID="uid") order by createdAt asc

私信内容表「notify_inbox_message」
表结构如下:

  1.  
    id: type: ‘integer‘, primaryKey: true, autoIncrement:true //编号;
  2.  
    senderID: type: ‘string‘, required: true //发送者编号;
  3.  
    content: type: ‘string‘, required: true //私信内容;
  4.  
    createdAt:type: ‘timestamp‘, required: true

参考

消息系统设计与实现 
通知系统设计

使用 socket.io node.js 和传入消息的通知系统的架构实现和设计

】使用socket.ionode.js和传入消息的通知系统的架构实现和设计【英文标题】:ArchitectureImplementationandDesignforaNotificationSystemusingsocket.ionode.jsandincomingmessages【发布时间】:2011-11-0305:11:21【问题描述】:免责声明我之前没有使用过node.js... 查看详情

laravel使用数据库队列给用户发送通知

...建控制器、创建模型、生成数据库对列表控制器用于发送消息模型用于消息的增删改查队列表用于存储队列任务4生成任务类执行异步发送消息5在User.php模型中,定义关联关系如下:6创建路由并运行队列进程7执行发送消息8数据... 查看详情

laravel使用数据库队列给用户发送通知

...建控制器、创建模型、生成数据库对列表控制器用于发送消息模型用于消息的增删改查队列表用于存储队列任务4生成任务类执行异步发送消息5在User.php模型中,定义关联关系如下:6创建路由并运行队列进程7执行发送消息8数据... 查看详情

系统逻辑架构设计

...述用例的主题,并显示一组对象间随着时间变化所交换的消息。消息包括异步信号和过程调用。顺序模型擅长显示系统用户所观察到的行为顺序。顺序模型有两种:场景和顺序图。顺序图具有更加结构化的形式。 3.设计与需... 查看详情

8_1_异步设计和站内邮件通知系统

一、需求描述1.利用Redis做消息队列,实现一个异步化服务框架;如图:2.利用搭建好的框架实现异步化发送点赞信息和登录异常信息。 二、具体diamante实现首先搭建应用Redis做消息队列的异步化框架 1.准备JedisAdapter.java类... 查看详情

私信通知 - 数据库表设计

...3:26【问题描述】:我已经创建了一个使用PHP和mySQL的私人消息传递系统,其通知有点像Facebook。数据库表有以下字段(未全部列出):消息ID发件人用户IDRecUserID留言主题日期时间状态-是否已读RepliedStatus-我应该如何使用它?De 查看详情

一文解密kafka,kafka源码设计与实现原理剖析,真正的通俗易懂

...AApacheKafka(简称Kafka)最早是由Linkedln开源出来的分布式消息系统,现在是Apache旗下的一个子项目,并且已经成为开册、领域应用最广泛的消息系统之Kafka社区也非常活跃,从版本开始,Kafka的标语已经从“一个高吞吐量、分布式... 查看详情

oppo手机没有消息通知

...图标>应用信息>通知管理」,开启允许通知开关;开启新消息通知(其他通知、音视频通话邀请通知)开关。3、关闭设为不重要通知开关/关闭移动至通知底部开关。a.ColorOS12版本路径:「长按应用图标>应用详情>通知管理>打开允... 查看详情

深入消息队列mq,看这篇就够了!

大厂面试爱问消息队列MQ。因为消息队列MQ,既是大型分布式系统不可缺少的中间件,也是高并发系统的基石中间件。如果你想要快速掌握消息队列MQ最内核的知识,以及消息队列MQ的主流应用场景、主流产品与选型、设计一个消... 查看详情

50架构实战:架构设计文档模板

...、目标、范围等]2.需求分析[5W1H8C]Who:需求利益干系人。消息队列系统主要是业务子系统来使用,子系统发送消息或者接收消息。When:需求使用时间。当子系统需要发送异步通知的时候,需要使用消息队列系统。What:需求的产... 查看详情

系统分析与设计复习

...的设计概述静态模型类图对象图接口包图动态模型:消息、顺序图、合作图消息顺序图合作图构件图和部署图系统体系结构模型构件图部署图设计模式组成设计原则分类创建型单例模式:原型模式:工厂方法模式࿱... 查看详情

架构文摘:消息队列设计精要

消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件,如老牌的ActiveMQ、RabbitM... 查看详情

javareview-实战eventbus设计模式

...,我们都会使用到MQ比如ApacheKafka等,某subscriber在消息中间件上注册了某个topic(主题),当有消息发送到了该topic上之后,注册在该topic上的所有subscriber都将会收到消息。消息中间件提供了系统之间的异步... 查看详情

消息中间件的使用场景

消息中间件的使用场景异构系统解耦(订单系统与库存系统解耦)并行处理任务(用户上传图片后,需要执行1图片缩放,2通知朋友,3奖励积分)异步处理任务(用户注册后,发送邮件通知)缓解数据洪峰(流量削峰)日志处理... 查看详情

消息传递系统-导论

向消费者通知新事件的常用方式消息传递系统(messagingsystem):Pro发送包含事件的消息,然后将消息推给Con。像Pro和Con之间的Unix管道或TCP连接这样的直接信道,是实现消息传递系统的简单方法。但大多消息传... 查看详情

消息传递系统-导论

向消费者通知新事件的常用方式消息传递系统(messagingsystem):Pro发送包含事件的消息,然后将消息推给Con。像Pro和Con之间的Unix管道或TCP连接这样的直接信道,是实现消息传递系统的简单方法。但大多消息传... 查看详情

设计简单的推送通知系统

】设计简单的推送通知系统【英文标题】:DesigningSimplePushNotificationsSystem【发布时间】:2017-04-1710:52:23【问题描述】:需要一些关于实施推送通知系统的设计建议。我使用后端Symfony3、Doctrine和postgres作为数据库。通知系统将有两... 查看详情

jdbc表设计哪个好

...据输入的通知系统1.电子邮件-通知应发送到的地址2.通知消息-varchar3.状态-发送与否(y或N)--请注意,最终目标是将所有通知发送到电子邮件并通过批处理作业将其作为一封电子邮件发送帮我选择更好的设计设计-1创建表通知(no... 查看详情