消息系统的设计

背景

在一个系统中,资源,数据会持续不断的更新。而用户如果需要知道这些数据的更新,就需要一个系统,将系统中不断更新的数据流,发送给相关的用户。

这个系统应该具备如下的几个功能:

  1. 可以根据配置信息,在资源,数据更新时,生成更新的消息。
  2. 可以将消息发送给相关的用户。
  3. 用户可以根据自定义的配置,配置是否接收消息推送。

针对这几个需求,来设计一个可灵活扩展的消息系统。

系统设计

将系统拆分成2个部分:

  1. 消息的产生
  2. 消息的发送

消息的产生

当资源的更新,触发了资源上的消息产生规则,就会产生消息。对于资源的更新,可能会触发多条消息产生规则,产生多条通知,发给不同的用户不同的消息。所以这里每条消息产生规则,对应一个消息模版。

消息的产生规则,由动作触发规则(rule),接受者(recipient)2部分组成。

  1. 动作触发规则是一个主谓短语(executor + action),或者主谓宾短语(executor + action + target),记录了谁对资源做了什么操作。消息产生规则作用于某一类的资源(executor / target)。这里的资源是一个抽象的对象类型。
  2. 接收者这里指的是某一个抽象的角色,描述了接受者与被操作资源或者动作执行者的关系。

比如:

  • xxx@我
rule recipient
somebody_@_somebody user
  • xxx评论了我的一条微博
rule recipient
somebody_comment_weibo author
  • xxx给我发了一条私信
rule recipient
somebody_send_message user
  • 你的好友xxx登录了
rule recipient
somebody_login friend

还有一种情况,当执行了某种操作,触发了多条条消息产生规则

比如:
当管理员A删除了一条微博,触发了2条消息产生规则

  • 系统删除了你的微博
rule recipient
somebody_delete_weibo author
  • 系统删除了你收藏的微博
rule recipient
somebody_delate_weibo follower

消息的发送

系统根据用户的订阅,将消息发送给订阅了消息的用户。

用户的订阅,是用户对具体资源(target / executor)上消息产生规则的订阅((executor + action) / executor + action + target)。这里用户订阅的是具体的资源对象。

比如:

  • xxx@我
特定资源 消息产生规则
我(target) somebody_@_someone
  • xxx评论了我的一条微博
特定资源 消息产生规则
我的微博(target) somebody_comment_weibo
  • xxx给我发了一条私信
特定资源 消息产生规则
我(target) sombody_send_message
  • 你的好友xxx登录了
特定资源 消息产生规则
好友(executor) somebody_login

消息的发送方式

消息的发送方式分为系统推送,和用户拉取

  1. 推 push:推是当消息产生时,系统自动将消息推送给订阅用户。
  2. 拉 pull:拉是当消息产生时,系统不自动将消息推送给用户,而是当用户主动触发拉取的动作时,获得新的消息。

系统运行发方式

当某个具体资源的更新,触发了这个资源上的消息产生规则,产生消息。系统再根据用户在这个资源上的订阅,将消息发送给订阅了这个资源的消息的用户。产生的消息和用户通过用户订阅关联起来。用户可以根据订阅的设置,来设置是否接收主动推送的消息。

  1. 用户设置好消息接收规则,确定哪些消息接收推送,哪些消息主动拉取
  2. 系统自动设置好各类资源的消息产生规则
  3. 资源在系统中创建时,系统根据用户的订阅规则,位用户订阅新资源的各类消息创建规则,生成用户订阅
  4. 当资源更新,触发了资源的消息产生规则,产生消息
  5. 系统根据用户的订阅,确定把哪些消息发送给用户,保存到用户消息列表中
  6. 系统根据用户设置的消息接收规则,决定是推送消息给用户,还是让用户拉取消息

系统建模

根据以上的分析,可以知道,系统中有以下几个实体类:

  1. 资源(target/executor)
资源可以是是系统中的各类对象模型

这里的资源可能是被操作的对象,也可能是动作的执行者。例如:

  • 好友登录:target 是 好友
  • 有人评论了文章:target 是 文章
  • 微博被删除:target 是 微博
  1. 消息产生规则(rule)/消息订阅规则
// 消息产生规则
[
  {
    'rule': 'user_update_weibo', // 触发规则
    'targetType': 'weibo', // 触发规则作用的对象类型
    'relationship': 'follow', // 触发对象与订阅对象的关系
    'role': 'user', // 订阅对象在系统中的角色
    'obtainType': 0 // 消息的发送方式:0.推,1.拉
  },
  {
    'rule': 'user_update_weibo',
    'targetType': 'weibo',
    'relationship': 'author',
    'role': 'user',
    'obtainType': 0
  },
  {
    'rule': 'user_comment_weibo',
    'targetType': 'weibo',
    'relationship': 'author',
    'role': 'user',
    'obtainType': 0
  },
  {
    'rule': 'user_delete_weibo',
    'targetType': 'weibo',
    'relationship': 'author',
    'role': 'user',
    'obtainType': 0
  }
  {
    'rule': 'user_delete_weibo',
    'targetType': 'weibo',
    'relationship': 'follow',
    'role': 'user',
    'obtainType': 0
  },
  {
    'rule': 'user_delete_weibo',
    'targetType': 'weibo',
    'relationship': 'admin',
    'role': 'admin',
    'obtainType': 1
  },
  {
    'rule': 'user_follow_someone',
    'targetType': 'user',
    'relationship': 'self',
    'role': 'user',
    'obtainType': 0
  },
  {
    'rule': 'user_publish_weibo',
    'targetType': 'user',
    'relationship': 'follow',
    'role': 'user',
    'obtainType': 0
  },
  {
    'rule': 'user_login',
    'targetType': 'user',
    'relationship': 'friend',
    'role': 'user',
    'obtainType': 0
  }
]

消息生成规则由如下几部分组成:

  • rule: 触发规则

触发规则用一个字符串表示,作为规则的唯一标识,也可以从字面上直接看出消息的规则,便于理解。每条规则是一个主谓短语(executor_action),或者主谓宾短语(executor_action_target)。
消息产生规则作用于某一类资源。但是对于不同的用户,和资源的关系不同,收到的消息不同,所以生成消息的规则也不同。所以通过用户和资源的关系,分组资源的消息产生规则。

  • targetType: 触发规则作用的对象类型

记录触发规则作用的对象类型,可以通过对象类型,查出这个对象上所有的触发规则。

  • relationship: 触发对象与订阅对象的关系

规则作用资源和订阅者之间的关联关系类别。当资源被创建时,或者用户和资源发生关联时,根据用户和资源的关系类型,订阅不同的消息规则。

  • role: 订阅对象在系统中的角色

对于不同角色的用户,对于同一种资源,需要配置的规则也不一样,比如不需要把管理员的订阅规则,保存到普通用户设置里。这里的role,可以和系统里的角色系统相关联,为不同的角色,配置不同的订阅规则。

  • obtainType: 消息的发送方式:0.推,1.拉

消息产生规则相对固定,并且每次增加,需要实现相应的消息模版,无法通过增加配置自动生效。所以这里直接使用配置文件或者配置类保存信息,比较简单方便,不需要对外提供管理编辑的接口。

  1. 消息(notify)
create table notify (
  id int,
  rule varchar comment '消息产生规则',
  obtain_type int comment '消息的获取方式:0.推,1.拉',
  target_id int comment '消息产生规则作用的对象',
  target_type int comment '消息产生规则作用的对象类型',
  content varchar comment '消息内容',
  sender_id int comment '消息发送者id',
  sender_type int comment '消息发送者类型:0.系统,1.用户,...',
  notify_type int comment '消息类型:0.公告,1.新闻,2.活动,3.feed,...',
  create_time timestamp comment '消息创建时间'
) comment = '消息'

说明下几个重点的字段:

  • target_id,可能是动作操作对象的id,也可能是动作执行者的id。
  • target_type,通过target_type,区分是不同的被操作对象还有动作执行者。
  • sender_id,当是系统发送的消息时,这里可以为空,或者某个特殊id。
  • sender_type,可以区分出是系统发送的还是用户发送的。
  • content,消息内容中动态的部分,可能需要动作执行者,动作,被操作对象,或者其他各种相关资源的数据来填充。因为需要的数据无法确定,所以这里交给每个消息产生规则的实现者取实现相应的消息模版。
  1. 消息接收者(recipient)
订阅消息的用户
  1. 订阅(subscribe)
create table subscribe (
  id int,
  rule int comment '消息产生规则',
  target_id int comment '消息产生规则作用的对象',
  target_type int comment '消息产生规则作用的对象类型',
  create_time timestamp comment '订阅时间',
  valid int comment '订阅是否有效:0.无效,1有效'
) comment = '用户订阅'
  1. 订阅设置(subscribeConfig)
保存具体用户订阅了哪些消息产生规则,以及针对这条规则,是否接收系统的主动推送
create table subscribe_config (
  id int,
  rule int comment '消息产生规则',
  enable_recieve int comment '针对这条规则,是否接收系统的主动推送:0.不接收,1.接收'
) comment = '用户订阅设置'
  1. 用户消息列表(recipientNotify)
create table recipient_notify (
  id int,
  recipient_id int comment '消息接收者id',
  notify_id int comment '消息id',
  create_time timestamp comment '消息创建时间',
  read_time timestamp comment '用户阅读时间'
) comment = '用户消息列表'

系统服务

根据系统的运行方式,和系统建模。系统的服务需要以下几个功能:

  • getAllRuleByObjectType(objectType) 获取某类对象的所有消息产生规则

  • setPushConfig(user) 用户设置获取推送规则

设置用户推送规则,是否接受推送

  • subscribe(user, rule, target) 用户订阅

设置用户订阅,将用户和消息产生规则和规则作用的具体对象关联

  • cancelSubscribe(user, rule, target) 取消用户订阅

取消用户订阅

  • listAllSubscribe(user) 获取用户的所有订阅

获取用户所有具体对象上的订阅

  • createNotify(rule, target, sender, source) 创建消息

根据消息产生规则,创建消息,source是消息内容需要的所有数据

  • pushNotify(receipient, notify) 推送消息到用户消息列表

将消息保存到用户消息列表,同时发送推送消息

  • pullNotify(receipient, notify) 拉取消息到用户消息列表

将消息保存到用户消息列表

  • getUserSubscribeConfig(user) 获取用户订阅规则

获取用户订阅的所有消息产生规则

  • getUsersBySubscribe(subscribe) 查询所有订阅了这条订阅的用户

  • getNotifyBySubscribe(subscribe) 查询所有根据这条消息产生规则和作用目标,生成的消息

  • readRecipientNotify(now) 读消息列表

设置读取的消息的读取时间

时序图
消息的创建,订阅,推送,拉取

Paste_Image.png
Paste_Image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容