背景
在一个系统中,资源,数据会持续不断的更新。而用户如果需要知道这些数据的更新,就需要一个系统,将系统中不断更新的数据流,发送给相关的用户。
这个系统应该具备如下的几个功能:
- 可以根据配置信息,在资源,数据更新时,生成更新的消息。
- 可以将消息发送给相关的用户。
- 用户可以根据自定义的配置,配置是否接收消息推送。
针对这几个需求,来设计一个可灵活扩展的消息系统。
系统设计
将系统拆分成2个部分:
- 消息的产生
- 消息的发送
消息的产生
当资源的更新,触发了资源上的消息产生规则,就会产生消息。对于资源的更新,可能会触发多条消息产生规则,产生多条通知,发给不同的用户不同的消息。所以这里每条消息产生规则,对应一个消息模版。
消息的产生规则,由动作触发规则(rule),接受者(recipient)2部分组成。
- 动作触发规则是一个主谓短语(executor + action),或者主谓宾短语(executor + action + target),记录了谁对资源做了什么操作。消息产生规则作用于某一类的资源(executor / target)。这里的资源是一个抽象的对象类型。
- 接收者这里指的是某一个抽象的角色,描述了接受者与被操作资源或者动作执行者的关系。
比如:
- 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 |
消息的发送方式
消息的发送方式分为系统推送,和用户拉取
- 推 push:推是当消息产生时,系统自动将消息推送给订阅用户。
- 拉 pull:拉是当消息产生时,系统不自动将消息推送给用户,而是当用户主动触发拉取的动作时,获得新的消息。
系统运行发方式
当某个具体资源的更新,触发了这个资源上的消息产生规则,产生消息。系统再根据用户在这个资源上的订阅,将消息发送给订阅了这个资源的消息的用户。产生的消息和用户通过用户订阅关联起来。用户可以根据订阅的设置,来设置是否接收主动推送的消息。
- 用户设置好消息接收规则,确定哪些消息接收推送,哪些消息主动拉取
- 系统自动设置好各类资源的消息产生规则
- 资源在系统中创建时,系统根据用户的订阅规则,位用户订阅新资源的各类消息创建规则,生成用户订阅
- 当资源更新,触发了资源的消息产生规则,产生消息
- 系统根据用户的订阅,确定把哪些消息发送给用户,保存到用户消息列表中
- 系统根据用户设置的消息接收规则,决定是推送消息给用户,还是让用户拉取消息
系统建模
根据以上的分析,可以知道,系统中有以下几个实体类:
- 资源(target/executor)
资源可以是是系统中的各类对象模型
这里的资源可能是被操作的对象,也可能是动作的执行者。例如:
- 好友登录:target 是 好友
- 有人评论了文章:target 是 文章
- 微博被删除:target 是 微博
- 消息产生规则(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.拉
消息产生规则相对固定,并且每次增加,需要实现相应的消息模版,无法通过增加配置自动生效。所以这里直接使用配置文件或者配置类保存信息,比较简单方便,不需要对外提供管理编辑的接口。
- 消息(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,消息内容中动态的部分,可能需要动作执行者,动作,被操作对象,或者其他各种相关资源的数据来填充。因为需要的数据无法确定,所以这里交给每个消息产生规则的实现者取实现相应的消息模版。
- 消息接收者(recipient)
订阅消息的用户
- 订阅(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 = '用户订阅'
- 订阅设置(subscribeConfig)
保存具体用户订阅了哪些消息产生规则,以及针对这条规则,是否接收系统的主动推送
create table subscribe_config (
id int,
rule int comment '消息产生规则',
enable_recieve int comment '针对这条规则,是否接收系统的主动推送:0.不接收,1.接收'
) comment = '用户订阅设置'
- 用户消息列表(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) 读消息列表
设置读取的消息的读取时间
时序图
消息的创建,订阅,推送,拉取