首先咱們來看一下市場上關於消息的實現是怎麼樣的。javascript
簡書的消息系統主要分了兩種java
簡信
簡信的性質其實跟私信是同樣的,是用戶發送給用戶的一則消息,有具體的信息內容。ios
提醒
而提醒,則是系統發送的一則消息,其文案格式是固定的,而且對特殊對象通常擁有超連接。json
知乎跟簡書同樣,主要分了兩種:數組
私信
跟簡書同樣,使用戶發送給用戶的一則消息,也能夠是管理員發送給用戶的消息。post
消息
知乎的消息比簡書的提醒有過之而無不及,知乎會對多條類似的消息進行聚會,以達到減輕用戶閱讀壓力的體驗。單元測試
經過兩種產品的簡單分析,得出他們的消息有兩種分類,在這基礎上,咱們再加上一種:公告。
公告的主要性質是系統發送一則含有具體內容的消息,站內全部用戶都能讀取到這條消息。
因此,消息有三種分類:測試
咱們從簡書取一組提醒樣本:網站
分析句子結構,提醒的內容無非就是ui
「誰對同樣屬於誰的事物作了什麼操做」
「someone do something in someone's something」
someone = 提醒的觸發者,或者發送者,標記爲sender
do something = 提醒的動做,評論、喜歡、關注都屬於一個動做,標記爲action
something = 提醒的動做做用對象,這就具體到是哪一篇文章,標記爲target
someone's = 提醒的動做做用對象的全部者,標記爲targetOwner
這就清楚了,sender和targetOwner就是網站的用戶,而target是具體到哪一篇文章,若是提醒的對象不只僅侷限於文章,還有其餘的話,就須要增長一項targetType,來標記目標是文章仍是其餘的什麼。而action,則是固定的,整個網站會觸發提醒的動做可能就只有那幾樣:評論、喜歡、關注.....(或者其餘業務須要提醒的動做)
以知乎爲例
推的比較常見,須要針對某一個問題維護着一張關注者的列表,每當觸發這個問題推送的條件時(例若有人回答問題),就把這個通知發送給每一個關注者。
拉的相對麻煩一點,就是推的反向,例如每一個用戶都有一張關注問題的列表,每當用戶上線的時候,對每一個問題進行輪詢,當問題的事件列表出現了比我本來時間戳大的信息就進行拉取。
而咱們則根據消息的不一樣分類採用不一樣的獲取方式:
通告和提醒,適合使用拉取的方式,消息產生以後,會存在消息表中,用戶在某一特定的時間根據本身關注問題的表進行消息的拉取,而後添加到本身的消息隊列中,
信息,適合使用推的方式,在發送者創建一條信息以後,同時指定接收者,把消息添加到接收者的消息隊列中。
根據提醒使用拉取的方式,須要維護一個關注某一事物的列表。
這種行爲,咱們稱之爲:「訂閱」Subscribe
一則訂閱有如下三個核心屬性:
好比我發佈了一篇文章,那麼我會訂閱文章《XXX》的評論動做,因此文章《XXX》每被人評論了,就須要發送一則提醒告知我。
訂閱的規則還能夠擴展
我喜歡了一篇文章,和我發佈了一篇文章,訂閱的動做可能不同。
喜歡了一篇文章,我但願我訂閱這篇文章更新、評論的動做。
而發佈了一篇文章,我但願我只是訂閱這篇文章的評論動做。
這時候就須要多一個參數:subscribReason
不一樣的subscribReason,對應着一個動做數組,
subscribReason = 喜歡,對應着 actions = [更新,評論]
subscribReason = 發佈,對應着 actions = [評論]
訂閱的規則還還能夠擴展
用戶可能會有一個本身的訂閱設置,好比對於全部的喜歡的動做,我都不但願接收。
好比Knewone的提醒設置
因此咱們須要再維護一個表:SubscriptionConfig,來存放用戶的提醒設置。
而且,當用戶沒有提醒設置的時候,可使用系統提供的一套默認設置:defaultSubscriptionConfig
若是我發佈了一篇文章《XXX》,在我不在線的時候,被評論了10遍,當我一上線的時候,應該是收到十條信息相似於:「誰誰誰評論了你的文章《XXX》」?
仍是應該收到一條信息:「甲、乙、丙、丁...評論了你的文章《XXX》」?
知乎在聚合上作的很優秀,要知道他們要實現這個仍是挺有技術的:
知乎的消息機制,在技術上如何設計與規劃?
網站的消息(通知)系統通常是如何實現的?
關於這部分功能,咱們尚未具體的實現方法,暫時也沒法講得更加詳細。⊙﹏⊙
經過上面的分析,大概知道作這個消息系統,須要哪些實體類:
說了這麼多,整理一下整個消息流程的一些行爲:
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 // 小明ID
Save Announce and Message
固然,Notify還支持存儲公告和信息。它們會用到content
字段,而不會用到target
、targetType
、action
字段。
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的建立,主要經過兩個途徑:
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下產生的每一條評論,都會產生通知給小明瞭。
action: {type: 'json', required: true}, // 用戶的設置 user: {type: 'integer'}
不一樣用戶可能會有不同的訂閱習慣,在這個表中,用戶能夠統一針對某種動做進行是否訂閱的設置。而默認是使用系統提供的默認配置:
defaultSubscriptionConfig: {
'comment' : true, // 評論 'like' : true, // 喜歡 }
在這套模型中,
targetType
、action
是能夠根據需求來擴展的,例如咱們還能夠增長多幾個動做的提醒:hate
被踩、update
被更新....諸如此類。
// 提醒關聯的目標類型 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, // 喜歡 }
createAnnounce(content, sender)
createRemind(target, targetType, action, sender, content)
createMessage(content, sender, receiver)
pullAnnounce(user)
lastTime
lastTime
做爲過濾條件,查詢Notify的公告信息pullRemind(user)
target
、targetType
、action
、createdAt
去查詢Notify表,獲取訂閱的Notify記錄。(注意訂閱時間必須早於提醒建立時間)subscribe(user, target, targetType, reason)
actions
cancelSubscription(user, target ,targetType)
user
、target
、targetType
對應的一則或多則記錄getSubscriptionConfig(userID)
updateSubscriptionConfig(userID)
getUserNotify(userID)
read(user, notifyIDs)
咱們能夠在產品建立以後,調用NotifyService.subscribe
方法,
而後在產品被評論以後調用NotifyService.createRemind
方法,
再就是用戶登陸系統或者其餘的某一個時刻調用NotifyService.pullRemind
方法,
最後在用戶查詢消息隊列的時候調用NotifyService.getUserNotify
方法。
在管理員發送了一則公告的時候,調用NotifyService.createAnnounce
方法,
而後在用戶登陸系統或者其餘的某一個時刻調用NotifyService.pullAnnounce
方法,
最後在用戶查詢消息隊列的時候調用NotifyService.getUserNotify
方法。
信息的建立,只須要直接調用NotifyService.createMessage
方法就能夠了,在下一次用戶查詢消息隊列的時候,就會查詢這條信息。