消息系統設計與實現

来源:http://www.cnblogs.com/dytl/archive/2016/03/30/5336917.html
-Advertisement-
Play Games

文/JC_Huang(簡書作者)原文鏈接:http://www.jianshu.com/p/f4d7827821f1著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。 產品分析 首先我們來看一下市場上關於消息的實現是怎麼樣的。 簡書 簡書的消息系統主要分了兩種 簡信 提醒 簡信簡信的性質 ...


文/JC_Huang(簡書作者)
原文鏈接:http://www.jianshu.com/p/f4d7827821f1
著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。

產品分析

首先我們來看一下市場上關於消息的實現是怎麼樣的。

簡書

簡書的消息系統主要分了兩種

  • 簡信
  • 提醒

簡信
簡信的性質其實跟私信是一樣的,是用戶發送給用戶的一則消息,有具體的信息內容。


簡書簡信

提醒
而提醒,則是系統發送的一則消息,其文案格式是固定的,並且對特殊對象一般擁有超鏈接。


簡書提醒

知乎

知乎跟簡書一樣,主要分了兩種:

  • 私信
  • 消息

私信
跟簡書一樣,使用戶發送給用戶的一則消息,也可以是管理員發送給用戶的消息。


知乎私信

消息
知乎的消息比簡書的提醒有過之而無不及,知乎會對多條相似的消息進行聚會,以達到減輕用戶閱讀壓力的體驗。


知乎消息

消息的三種分類

通過兩種產品的簡單分析,得出他們的消息有兩種分類,在這基礎上,我們再加上一種:公告。
公告的主要性質是系統發送一則含有具體內容的消息,站內所有用戶都能讀取到這條消息。
所以,消息有三種分類:

  1. 公告 Announce
  2. 提醒 Remind
  3. 私信 Message

提醒的語言分析

我們從簡書取一組提醒樣本:

  • 3dbe1bd90774 關註了你
  • magicdawn 喜歡了你的文章 《單點登錄的三種實現方式》
  • 無良程式 喜歡了你的文章 《基於RESTful API 怎麼設計用戶許可權控制?》
  • alexcc4 喜歡了你的文章 《在Nodejs中貫徹單元測試》
  • 你在《基於RESTful API 怎麼設計用戶許可權控制?》中收到一條 cnlinjie 的評論
  • 你的文章《Session原理》已被加入專題 《ios開發》

分析句子結構,提醒的內容無非就是

「誰對一樣屬於誰的事物做了什麼操作」
「someone do something in someone's something」

someone = 提醒的觸發者,或者發送者,標記為sender
do something = 提醒的動作,評論、喜歡、關註都屬於一個動作,標記為action
something = 提醒的動作作用對象,這就具體到是哪一篇文章,標記為target
someone's = 提醒的動作作用對象的所有者,標記為targetOwner

這就清楚了,sender和targetOwner就是網站的用戶,而target是具體到哪一篇文章,如果提醒的對象不僅僅局限於文章,還有其他的話,就需要增加一項targetType,來標記目標是文章還是其他的什麼。而action,則是固定的,整個網站會觸發提醒的動作可能就只有那幾樣:評論、喜歡、關註.....(或者其他業務需要提醒的動作)

消息的兩種獲取方式

  • 推 Push
  • 拉 Pull

以知乎為例
推的比較常見,需要針對某一個問題維護著一張關註者的列表,每當觸發這個問題推送的條件時(例如有人回答問題),就把這個通知發送給每個關註者。

拉的相對麻煩一點,就是推的反向,例如每個用戶都有一張關註問題的列表,每當用戶上線的時候,對每個問題進行輪詢,當問題的事件列表出現了比我原本時間戳大的信息就進行拉取。

而我們則根據消息的不同分類採用不同的獲取方式
通告和提醒,適合使用拉取的方式,消息產生之後,會存在消息表中,用戶在某一特定的時間根據自己關註問題的表進行消息的拉取,然後添加到自己的消息隊列中,

信息,適合使用推的方式,在發送者建立一條信息之後,同時指定接收者,把消息添加到接收者的消息隊列中。

訂閱

根據提醒使用拉取的方式,需要維護一個關註某一事物的列表。
這種行為,我們稱之為:「訂閱」Subscribe

一則訂閱有以下三個核心屬性

  • 訂閱的目標 target
  • 訂閱的目標類型 targetType
  • 訂閱的動作 action

比如我發佈了一篇文章,那麼我會訂閱文章《XXX》的評論動作,所以文章《XXX》每被人評論了,就需要發送一則提醒告知我。

訂閱的規則還可以擴展
我喜歡了一篇文章,和我發佈了一篇文章,訂閱的動作可能不一樣。
喜歡了一篇文章,我希望我訂閱這篇文章更新、評論的動作。
而發佈了一篇文章,我希望我只是訂閱這篇文章的評論動作。

這時候就需要多一個參數:subscribReason
不同的subscribReason,對應著一個動作數組,
subscribReason = 喜歡,對應著 actions = [更新,評論]
subscribReason = 發佈,對應著 actions = [評論]

訂閱的規則還還可以擴展
用戶可能會有一個自己的訂閱設置,比如對於所有的喜歡的動作,我都不希望接收。
比如Knewone的提醒設置


Knewone提醒設置

所以我們需要再維護一個表:SubscriptionConfig,來存放用戶的提醒設置。
並且,當用戶沒有提醒設置的時候,可以使用系統提供的一套預設設置:defaultSubscriptionConfig

聚合

如果我發佈了一篇文章《XXX》,在我不線上的時候,被評論了10遍,當我一上線的時候,應該是收到十條信息類似於:「誰誰誰評論了你的文章《XXX》」?
還是應該收到一條信息:「甲、乙、丙、丁...評論了你的文章《XXX》」?

知乎在聚合上做的很優秀,要知道他們要實現這個還是挺有技術的:
知乎的消息機制,在技術上如何設計與規劃?
網站的消息(通知)系統一般是如何實現的?

關於這部分功能,我們還沒有具體的實現方法,暫時也無法講得更加詳細。⊙﹏⊙

五個實體

通過上面的分析,大概知道做這個消息系統,需要哪些實體類:

  1. 用戶消息隊列 UserNotify
  2. 用戶 User
  3. 訂閱 Subscription
  4. 訂閱設置 SubscriptionConfig
  5. 消息 Notify
    • 通告 Announce
    • 提醒 Remind
    • 信息 Message

行為分解

說了這麼多,整理一下整個消息流程的一些行為:

  • 系統或者管理員,創建消息
    • createNotify (make announce | remind | message)
  • 用戶,訂閱消息,取消訂閱
    • subscribe, cancelSubscription
  • 用戶管理訂閱設置
    • getSubscriptionConfig, updateSubscriptionConfig
  • 用戶,拉取消息
    • pullNotify (pull announce | remind | message | all)
  • 用戶,查詢消息隊列
    • getUserNotify(get announce | remind | message | all)
  • 用戶閱讀消息
    • read
  >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

模型設計

Notify

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
消息表,我們需要targettargetType欄位,來記錄該條提醒所關聯的對象。而action欄位,則記錄該條提醒所關聯的動作。
比如消息:「小明喜歡了文章」
則:

target = 123,  // 文章ID
targetType = 'post',  // 指明target所屬類型是文章
sender = 123456  // 小明ID

Save Announce and Message
當然,Notify還支持存儲公告和信息。它們會用到content欄位,而不會用到targettargetTypeaction欄位。

UserNotify

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的創建,主要通過兩個途徑:

  1. 遍歷訂閱(Subscription)表拉取公告(Announce)和提醒(Remind)的時候創建
  2. 新建信息(Message)之後,立刻創建。

Subscription

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下產生的每一條評論,都會產生通知給小明瞭。

SubscriptionConfig

action: {type: 'json', required: true},   // 用戶的設置
user: {type: 'integer'}

不同用戶可能會有不一樣的訂閱習慣,在這個表中,用戶可以統一針對某種動作進行是否訂閱的設置。而預設是使用系統提供的預設配置:

defaultSubscriptionConfig: {
  'comment'   : true,    // 評論
  'like'      : true,    // 喜歡
}

在這套模型中,targetTypeaction是可以根據需求來擴展的,例如我們還可以增加多幾個動作的提醒:hate被踩、update被更新....諸如此類。

配置文件 NotifyConfig

// 提醒關聯的目標類型
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,    // 喜歡
}

服務層 NotifyService

NotifyService擁有以下方法:

  • createAnnounce(content, sender)
  • createRemind(target, targetType, action, sender, content)
  • createMessage(content, sender, receiver)
  • pullAnnounce(user)
  • pullRemind(user)
  • subscribe(user, target, targetType, reason)
  • cancelSubscription(user, target ,targetType)
  • getSubscriptionConfig(userID)
  • updateSubscriptionConfig(userID)
  • getUserNotify(userID)
  • read(user, notifyIDs)

各方法的處理邏輯如下:

createAnnounce(content, sender)

  1. 往Notify表中插入一條公告記錄

createRemind(target, targetType, action, sender, content)

  1. 往Notify表中插入一條提醒記錄

createMessage(content, sender, receiver)

  1. 往Notify表中插入一條信息記錄
  2. 往UserNotify表中插入一條記錄,並關聯新建的Notify

pullAnnounce(user)

  1. 從UserNotify中獲取最近的一條公告信息的創建時間: lastTime
  2. lastTime作為過濾條件,查詢Notify的公告信息
  3. 新建UserNotify並關聯查詢出來的公告信息

pullRemind(user)

  1. 查詢用戶的訂閱表,得到用戶的一系列訂閱記錄
  2. 通過每一條的訂閱記錄的targettargetTypeactioncreatedAt去查詢Notify表,獲取訂閱的Notify記錄。(註意訂閱時間必須早於提醒創建時間)
  3. 查詢用戶的配置文件SubscriptionConfig,如果沒有則使用預設的配置DefaultSubscriptionConfig
  4. 使用訂閱配置,過濾查詢出來的Notify
  5. 使用過濾好的Notify作為關聯新建UserNotify

subscribe(user, target, targetType, reason)

  1. 通過reason,查詢NotifyConfig,獲取對應的動作組:actions
  2. 遍歷動作組,每一個動作新建一則Subscription記錄

cancelSubscription(user, target ,targetType)

  1. 刪除usertargettargetType對應的一則或多則記錄

getSubscriptionConfig(userID)

  1. 查詢SubscriptionConfig表,獲取用戶的訂閱配置

updateSubscriptionConfig(userID)

  1. 更新用戶的SubscriptionConfig記錄

getUserNotify(userID)

  1. 獲取用戶的消息列表

read(user, notifyIDs)

  1. 更新指定的notify,把isRead屬性設置為true

時序圖

提醒的訂閱、創建、拉取


提醒的訂閱、創建、拉取


我們可以在產品創建之後,調用NotifyService.subscribe方法,
然後在產品被評論之後調用NotifyService.createRemind方法,
再就是用戶登錄系統或者其他的某一個時刻調用NotifyService.pullRemind方法,
最後在用戶查詢消息隊列的時候調用NotifyService.getUserNotify方法。

公告的創建、拉取


公告的創建、拉取


在管理員發送了一則公告的時候,調用NotifyService.createAnnounce方法,
然後在用戶登錄系統或者其他的某一個時刻調用NotifyService.pullAnnounce方法,
最後在用戶查詢消息隊列的時候調用NotifyService.getUserNotify方法。

信息的創建


信息的創建


信息的創建,只需要直接調用NotifyService.createMessage方法就可以了,
在下一次用戶查詢消息隊列的時候,就會查詢這條信息。


 

   
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. Requirements: when we use the sql like "select * from targetTable", we get all records of the table, but we usually just need one page of records(a ...
  • 項目地址:https://github.com/dianping/cat 編譯步驟: 這個項目比較另類,把編譯需要的jar包,單獨放在git分支mvn-repo里了,而且官方文檔里給了一個錯誤的命令提示: 當你直接把這條命令貼到terminal里執行時,會直接提示命令無效,正確的姿勢如下: 1、先安 ...
  • 1 設計思路 為了設計一套具有較強可擴展性的用戶認證管理,需要建立用戶、角色和許可權等資料庫表,並且建立之間的關係,具體實現如下。 1.1 用戶 用戶僅僅是純粹的用戶,用來記錄用戶相關信息,如用戶名、密碼等,許可權是被分離出去了的。用戶(User)要擁有對某種資源的許可權,必須通過角色(Role)去關聯。 ...
  • 0x 00 前言 ZoomEye 的 API 在前幾天正式對外部開發,這對網路滲透人員來說是一件開心的事 可以說“媽媽再也不用擔心批量測(x)試(zhan)沒有資源了。” 官方的 API 幫助文檔在下麵: https://www.zoomeye.org/api/ 看了下,使用方法是先提交賬戶,密碼獲 ...
  • 1.決策樹的簡介 http://www.cnblogs.com/lufangtao/archive/2013/05/30/3103588.html 2.決策是實現的偽代碼 3.python數據結構設計 1.數據集:用於存儲二維的訓練數據training_data 二維的list數組,對於二維的lis ...
  • HashMap實現了Map介面,HashTable是Dictionary的子類; 主要區別有以下三點: 1.HashMap允許空的鍵值,也就是說 key 可以為 null(只能有一個key為null),而HashTable不可以; 2.HashMap不同步的,在多線程訪問時,需要為它的方法實現同步S ...
  • 實現了任意大數與 2^64-1以下的數相乘, 兩個任意大數可以將其中一個拆分成多個因數, 兩個大數質數暫未考慮 ...
  • 最近在學習PKI,順便接觸了一些加密演算法。對RSA著重研究了一下,自己也寫了一個簡單的實現RSA演算法的Demo,包括公、私鑰生成,加解密的實現。雖然比較簡單,但是也大概囊括了RSA加解密的核心思想與流程。這裡寫下來與大家分享一下。 RSA概述: RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...