背景
在一個(gè)系統(tǒng)中杖们,資源,數(shù)據(jù)會(huì)持續(xù)不斷的更新肩狂。而用戶如果需要知道這些數(shù)據(jù)的更新摘完,就需要一個(gè)系統(tǒng),將系統(tǒng)中不斷更新的數(shù)據(jù)流傻谁,發(fā)送給相關(guān)的用戶孝治。
這個(gè)系統(tǒng)應(yīng)該具備如下的幾個(gè)功能:
- 可以根據(jù)配置信息,在資源审磁,數(shù)據(jù)更新時(shí)谈飒,生成更新的消息。
- 可以將消息發(fā)送給相關(guān)的用戶态蒂。
- 用戶可以根據(jù)自定義的配置杭措,配置是否接收消息推送。
針對(duì)這幾個(gè)需求钾恢,來(lái)設(shè)計(jì)一個(gè)可靈活擴(kuò)展的消息系統(tǒng)手素。
系統(tǒng)設(shè)計(jì)
將系統(tǒng)拆分成2個(gè)部分:
- 消息的產(chǎn)生
- 消息的發(fā)送
消息的產(chǎn)生
當(dāng)資源的更新,觸發(fā)了資源上的消息產(chǎn)生規(guī)則瘩蚪,就會(huì)產(chǎn)生消息泉懦。對(duì)于資源的更新,可能會(huì)觸發(fā)多條消息產(chǎn)生規(guī)則疹瘦,產(chǎn)生多條通知崩哩,發(fā)給不同的用戶不同的消息。所以這里每條消息產(chǎn)生規(guī)則言沐,對(duì)應(yīng)一個(gè)消息模版琢锋。
消息的產(chǎn)生規(guī)則辕漂,由動(dòng)作觸發(fā)規(guī)則(rule),接受者(recipient)2部分組成吴超。
- 動(dòng)作觸發(fā)規(guī)則是一個(gè)主謂短語(yǔ)(executor + action)钉嘹,或者主謂賓短語(yǔ)(executor + action + target),記錄了誰(shuí)對(duì)資源做了什么操作鲸阻。消息產(chǎn)生規(guī)則作用于某一類(lèi)的資源(executor / target)跋涣。這里的資源是一個(gè)抽象的對(duì)象類(lèi)型。
- 接收者這里指的是某一個(gè)抽象的角色鸟悴,描述了接受者與被操作資源或者動(dòng)作執(zhí)行者的關(guān)系陈辱。
比如:
- xxx@我
rule | recipient |
---|---|
somebody_@_somebody | user |
- xxx評(píng)論了我的一條微博
rule | recipient |
---|---|
somebody_comment_weibo | author |
- xxx給我發(fā)了一條私信
rule | recipient |
---|---|
somebody_send_message | user |
- 你的好友xxx登錄了
rule | recipient |
---|---|
somebody_login | friend |
還有一種情況,當(dāng)執(zhí)行了某種操作细诸,觸發(fā)了多條條消息產(chǎn)生規(guī)則
比如:
當(dāng)管理員A刪除了一條微博沛贪,觸發(fā)了2條消息產(chǎn)生規(guī)則
- 系統(tǒng)刪除了你的微博
rule | recipient |
---|---|
somebody_delete_weibo | author |
- 系統(tǒng)刪除了你收藏的微博
rule | recipient |
---|---|
somebody_delate_weibo | follower |
消息的發(fā)送
系統(tǒng)根據(jù)用戶的訂閱,將消息發(fā)送給訂閱了消息的用戶震贵。
用戶的訂閱利赋,是用戶對(duì)具體資源(target / executor)上消息產(chǎn)生規(guī)則的訂閱((executor + action) / executor + action + target)。這里用戶訂閱的是具體的資源對(duì)象猩系。
比如:
- xxx@我
特定資源 | 消息產(chǎn)生規(guī)則 |
---|---|
我(target) | somebody_@_someone |
- xxx評(píng)論了我的一條微博
特定資源 | 消息產(chǎn)生規(guī)則 |
---|---|
我的微博(target) | somebody_comment_weibo |
- xxx給我發(fā)了一條私信
特定資源 | 消息產(chǎn)生規(guī)則 |
---|---|
我(target) | sombody_send_message |
- 你的好友xxx登錄了
特定資源 | 消息產(chǎn)生規(guī)則 |
---|---|
好友(executor) | somebody_login |
消息的發(fā)送方式
消息的發(fā)送方式分為系統(tǒng)推送媚送,和用戶拉取
- 推 push:推是當(dāng)消息產(chǎn)生時(shí)准颓,系統(tǒng)自動(dòng)將消息推送給訂閱用戶厕妖。
- 拉 pull:拉是當(dāng)消息產(chǎn)生時(shí),系統(tǒng)不自動(dòng)將消息推送給用戶易稠,而是當(dāng)用戶主動(dòng)觸發(fā)拉取的動(dòng)作時(shí)拿霉,獲得新的消息吟秩。
系統(tǒng)運(yùn)行發(fā)方式
當(dāng)某個(gè)具體資源的更新,觸發(fā)了這個(gè)資源上的消息產(chǎn)生規(guī)則绽淘,產(chǎn)生消息峰尝。系統(tǒng)再根據(jù)用戶在這個(gè)資源上的訂閱,將消息發(fā)送給訂閱了這個(gè)資源的消息的用戶收恢。產(chǎn)生的消息和用戶通過(guò)用戶訂閱關(guān)聯(lián)起來(lái)。用戶可以根據(jù)訂閱的設(shè)置祭往,來(lái)設(shè)置是否接收主動(dòng)推送的消息伦意。
- 用戶設(shè)置好消息接收規(guī)則,確定哪些消息接收推送硼补,哪些消息主動(dòng)拉取
- 系統(tǒng)自動(dòng)設(shè)置好各類(lèi)資源的消息產(chǎn)生規(guī)則
- 資源在系統(tǒng)中創(chuàng)建時(shí)驮肉,系統(tǒng)根據(jù)用戶的訂閱規(guī)則,位用戶訂閱新資源的各類(lèi)消息創(chuàng)建規(guī)則已骇,生成用戶訂閱
- 當(dāng)資源更新离钝,觸發(fā)了資源的消息產(chǎn)生規(guī)則票编,產(chǎn)生消息
- 系統(tǒng)根據(jù)用戶的訂閱,確定把哪些消息發(fā)送給用戶卵渴,保存到用戶消息列表中
- 系統(tǒng)根據(jù)用戶設(shè)置的消息接收規(guī)則慧域,決定是推送消息給用戶,還是讓用戶拉取消息
系統(tǒng)建模
根據(jù)以上的分析浪读,可以知道昔榴,系統(tǒng)中有以下幾個(gè)實(shí)體類(lèi):
- 資源(target/executor)
資源可以是是系統(tǒng)中的各類(lèi)對(duì)象模型
這里的資源可能是被操作的對(duì)象,也可能是動(dòng)作的執(zhí)行者碘橘。例如:
- 好友登錄:target 是 好友
- 有人評(píng)論了文章:target 是 文章
- 微博被刪除:target 是 微博
- 消息產(chǎn)生規(guī)則(rule)/消息訂閱規(guī)則
// 消息產(chǎn)生規(guī)則
[
{
'rule': 'user_update_weibo', // 觸發(fā)規(guī)則
'targetType': 'weibo', // 觸發(fā)規(guī)則作用的對(duì)象類(lèi)型
'relationship': 'follow', // 觸發(fā)對(duì)象與訂閱對(duì)象的關(guān)系
'role': 'user', // 訂閱對(duì)象在系統(tǒng)中的角色
'obtainType': 0 // 消息的發(fā)送方式: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
}
]
消息生成規(guī)則由如下幾部分組成:
- rule: 觸發(fā)規(guī)則
觸發(fā)規(guī)則用一個(gè)字符串表示,作為規(guī)則的唯一標(biāo)識(shí)痘拆,也可以從字面上直接看出消息的規(guī)則仰禽,便于理解。每條規(guī)則是一個(gè)主謂短語(yǔ)(executor_action)纺蛆,或者主謂賓短語(yǔ)(executor_action_target)吐葵。
消息產(chǎn)生規(guī)則作用于某一類(lèi)資源。但是對(duì)于不同的用戶犹撒,和資源的關(guān)系不同折联,收到的消息不同,所以生成消息的規(guī)則也不同识颊。所以通過(guò)用戶和資源的關(guān)系诚镰,分組資源的消息產(chǎn)生規(guī)則。
- targetType: 觸發(fā)規(guī)則作用的對(duì)象類(lèi)型
記錄觸發(fā)規(guī)則作用的對(duì)象類(lèi)型祥款,可以通過(guò)對(duì)象類(lèi)型清笨,查出這個(gè)對(duì)象上所有的觸發(fā)規(guī)則。
- relationship: 觸發(fā)對(duì)象與訂閱對(duì)象的關(guān)系
規(guī)則作用資源和訂閱者之間的關(guān)聯(lián)關(guān)系類(lèi)別刃跛。當(dāng)資源被創(chuàng)建時(shí)抠艾,或者用戶和資源發(fā)生關(guān)聯(lián)時(shí),根據(jù)用戶和資源的關(guān)系類(lèi)型桨昙,訂閱不同的消息規(guī)則检号。
- role: 訂閱對(duì)象在系統(tǒng)中的角色
對(duì)于不同角色的用戶,對(duì)于同一種資源蛙酪,需要配置的規(guī)則也不一樣齐苛,比如不需要把管理員的訂閱規(guī)則,保存到普通用戶設(shè)置里桂塞。這里的role凹蜂,可以和系統(tǒng)里的角色系統(tǒng)相關(guān)聯(lián),為不同的角色,配置不同的訂閱規(guī)則玛痊。
- obtainType: 消息的發(fā)送方式:0.推汰瘫,1.拉
消息產(chǎn)生規(guī)則相對(duì)固定,并且每次增加擂煞,需要實(shí)現(xiàn)相應(yīng)的消息模版混弥,無(wú)法通過(guò)增加配置自動(dòng)生效。所以這里直接使用配置文件或者配置類(lèi)保存信息颈娜,比較簡(jiǎn)單方便剑逃,不需要對(duì)外提供管理編輯的接口。
- 消息(notify)
create table notify (
id int,
rule varchar comment '消息產(chǎn)生規(guī)則',
obtain_type int comment '消息的獲取方式:0.推官辽,1.拉',
target_id int comment '消息產(chǎn)生規(guī)則作用的對(duì)象',
target_type int comment '消息產(chǎn)生規(guī)則作用的對(duì)象類(lèi)型',
content varchar comment '消息內(nèi)容',
sender_id int comment '消息發(fā)送者id',
sender_type int comment '消息發(fā)送者類(lèi)型:0.系統(tǒng)蛹磺,1.用戶,...',
notify_type int comment '消息類(lèi)型:0.公告同仆,1.新聞萤捆,2.活動(dòng),3.feed俗批,...',
create_time timestamp comment '消息創(chuàng)建時(shí)間'
) comment = '消息'
說(shuō)明下幾個(gè)重點(diǎn)的字段:
- target_id俗或,可能是動(dòng)作操作對(duì)象的id,也可能是動(dòng)作執(zhí)行者的id岁忘。
- target_type辛慰,通過(guò)target_type,區(qū)分是不同的被操作對(duì)象還有動(dòng)作執(zhí)行者干像。
- sender_id帅腌,當(dāng)是系統(tǒng)發(fā)送的消息時(shí),這里可以為空麻汰,或者某個(gè)特殊id速客。
- sender_type,可以區(qū)分出是系統(tǒng)發(fā)送的還是用戶發(fā)送的五鲫。
- content溺职,消息內(nèi)容中動(dòng)態(tài)的部分,可能需要?jiǎng)幼鲌?zhí)行者位喂,動(dòng)作浪耘,被操作對(duì)象,或者其他各種相關(guān)資源的數(shù)據(jù)來(lái)填充塑崖。因?yàn)樾枰臄?shù)據(jù)無(wú)法確定七冲,所以這里交給每個(gè)消息產(chǎn)生規(guī)則的實(shí)現(xiàn)者取實(shí)現(xiàn)相應(yīng)的消息模版。
- 消息接收者(recipient)
訂閱消息的用戶
- 訂閱(subscribe)
create table subscribe (
id int,
rule int comment '消息產(chǎn)生規(guī)則',
target_id int comment '消息產(chǎn)生規(guī)則作用的對(duì)象',
target_type int comment '消息產(chǎn)生規(guī)則作用的對(duì)象類(lèi)型',
create_time timestamp comment '訂閱時(shí)間',
valid int comment '訂閱是否有效:0.無(wú)效弃舒,1有效'
) comment = '用戶訂閱'
- 訂閱設(shè)置(subscribeConfig)
保存具體用戶訂閱了哪些消息產(chǎn)生規(guī)則,以及針對(duì)這條規(guī)則,是否接收系統(tǒng)的主動(dòng)推送
create table subscribe_config (
id int,
rule int comment '消息產(chǎn)生規(guī)則',
enable_recieve int comment '針對(duì)這條規(guī)則聋呢,是否接收系統(tǒng)的主動(dòng)推送:0.不接收苗踪,1.接收'
) comment = '用戶訂閱設(shè)置'
- 用戶消息列表(recipientNotify)
create table recipient_notify (
id int,
recipient_id int comment '消息接收者id',
notify_id int comment '消息id',
create_time timestamp comment '消息創(chuàng)建時(shí)間',
read_time timestamp comment '用戶閱讀時(shí)間'
) comment = '用戶消息列表'
系統(tǒng)服務(wù)
根據(jù)系統(tǒng)的運(yùn)行方式,和系統(tǒng)建模削锰。系統(tǒng)的服務(wù)需要以下幾個(gè)功能:
getAllRuleByObjectType(objectType) 獲取某類(lèi)對(duì)象的所有消息產(chǎn)生規(guī)則
setPushConfig(user) 用戶設(shè)置獲取推送規(guī)則
設(shè)置用戶推送規(guī)則通铲,是否接受推送
- subscribe(user, rule, target) 用戶訂閱
設(shè)置用戶訂閱,將用戶和消息產(chǎn)生規(guī)則和規(guī)則作用的具體對(duì)象關(guān)聯(lián)
- cancelSubscribe(user, rule, target) 取消用戶訂閱
取消用戶訂閱
- listAllSubscribe(user) 獲取用戶的所有訂閱
獲取用戶所有具體對(duì)象上的訂閱
- createNotify(rule, target, sender, source) 創(chuàng)建消息
根據(jù)消息產(chǎn)生規(guī)則器贩,創(chuàng)建消息颅夺,source是消息內(nèi)容需要的所有數(shù)據(jù)
- pushNotify(receipient, notify) 推送消息到用戶消息列表
將消息保存到用戶消息列表,同時(shí)發(fā)送推送消息
- pullNotify(receipient, notify) 拉取消息到用戶消息列表
將消息保存到用戶消息列表
- getUserSubscribeConfig(user) 獲取用戶訂閱規(guī)則
獲取用戶訂閱的所有消息產(chǎn)生規(guī)則
getUsersBySubscribe(subscribe) 查詢所有訂閱了這條訂閱的用戶
getNotifyBySubscribe(subscribe) 查詢所有根據(jù)這條消息產(chǎn)生規(guī)則和作用目標(biāo)蛹稍,生成的消息
readRecipientNotify(now) 讀消息列表
設(shè)置讀取的消息的讀取時(shí)間
時(shí)序圖
消息的創(chuàng)建吧黄,訂閱,推送唆姐,拉取