日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

以 B 站为例,聊聊站内消息系统的设计

發(fā)布時間:2023/12/29 综合教程 34 生活家
生活随笔 收集整理的這篇文章主要介紹了 以 B 站为例,聊聊站内消息系统的设计 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

使用過簡書,知乎或 b 站的小伙伴應(yīng)該都有這樣的使用體驗(yàn):當(dāng)有其他用戶關(guān)注我們或者私信我們的行為時,我們會收到相關(guān)的消息。雖然這些功能看上去簡單,但其背后的設(shè)計(jì)是非常復(fù)雜的,幾乎是一個完成的系統(tǒng),可以稱之為站內(nèi)消息系統(tǒng)。

我以 b 站舉例(個人認(rèn)為 b 站的消息系統(tǒng)是我見過的非常完美的,UI 也最為人性化的):

b站站內(nèi)消息

可以看到 b 站把消息大致分為了三類:

系統(tǒng)推送的通知(System Notice);

回復(fù)、@、點(diǎn)贊等用戶行為產(chǎn)生的提醒(Remind);

用戶之間的私信(Chat)。

這樣設(shè)計(jì)不僅分類明確,且處于同一個主體的事件提醒還會做一個聚合,極大的提高了用戶體驗(yàn),不讓用戶收到太多分散的消息。

舉個例子:比如你在某個視頻或某篇文章下發(fā)表了評論,有 100 個人給你的評論點(diǎn)了贊,那么你希望消息頁面呈現(xiàn)的是一個一個用戶給你點(diǎn)贊的提醒,還是像以下聚合之后的提醒:

消息的聚合

我相信你大概率會選擇后者。

我認(rèn)為對于很多應(yīng)用來說,這樣的設(shè)計(jì)都是非常合理的,接下來我寫寫我對于消息系統(tǒng)的設(shè)計(jì)。

系統(tǒng)通知(System Notice)

系統(tǒng)通知一般是由后臺管理員發(fā)出,然后指定某一類(全體,個人等)用戶接收。基于此設(shè)想,可以把系統(tǒng)通知大致分為兩張表:

t_manager_system_notice(管理員系統(tǒng)通知表):記錄管理員發(fā)出的通知 ;

t_user_system_notice(用戶系統(tǒng)通知表):存儲用戶接受的通知。

t_manager_system_notice 結(jié)構(gòu)如下:

字段名類型描述system_notice_idLONG系統(tǒng)通知 IDtitleVARCHAR標(biāo)題contentTEXT內(nèi)容typeVARCHAR發(fā)給哪些用戶:單用戶 single;全體用戶 all,vip 用戶,具體類型各位小伙伴可以根據(jù)自己的需求選擇stateBOOLEAN是否已被拉取過,如果已經(jīng)拉取過,就無需再次拉取recipient_idLONG接受通知的用戶的 ID,如果 type 為單用戶,那么 recipient 為該用戶的 ID;否則 recipient 為 0manager_idLONG發(fā)布通知的管理員 IDpublish_timeTIMESTAMP發(fā)布時間

t_user_system_notice 結(jié)構(gòu)如下:

字段名類型描述user_notice_idLONG主鍵 IDstateBOOLEAN是否已讀system_notice_idLONG系統(tǒng)通知的 IDrecipient_idLONG接受通知的用戶的 IDpull_timeTIMESTAMP拉取通知的時間

當(dāng)管理員發(fā)布一條通知后,將通知插入 t_manager_system_notice 表中,然后系統(tǒng)定時的從 t_manager_system_notice 表中拉取通知,然后根據(jù)通知的 type 將通知插入 t_user_system_notice 表中。

如果通知的 type 是 single 的,那就只需要插入一條記錄到 t_user_system_notice 中。如果是全體用戶,那么就需要將一個通知批量根據(jù)不同的用戶 ID 插入到 t_user_system_notice 中,這個數(shù)據(jù)量就需要根據(jù)平臺的用戶量來計(jì)算。

舉個例子:管理員 A 發(fā)布了一個活動的通知,他需要將這個通知發(fā)布給全體用戶,當(dāng)拉取時間到來時,系統(tǒng)會將這一條通知取出。隨后系統(tǒng)到用戶表中查詢選取所有用戶的 ID,然后將這一條通知的信息根據(jù)所有用戶的 ID,批量插入 t_user_system_notice 中。用戶需要查看系統(tǒng)通知時,從 t_user_system_notice 表中查詢就行了。

注意:

因?yàn)橐淮卫〉臄?shù)據(jù)量可能很大,所以兩次拉取的時間間隔可以設(shè)置的長一些。

拉取 t_manager_system_notice 表中的通知時,需要判斷 state,如果已經(jīng)拉取過,就不需要重復(fù)拉取, 否則會造成重復(fù)消費(fèi)。

當(dāng)一條通知需要發(fā)布給全體用戶時,我們應(yīng)該考慮到用戶的活躍度。因?yàn)槿绻行┯脩糸L期不活躍, 我們還將通知推送給他(她),這顯然會造成空間的浪費(fèi)。所以在選取用戶 ID 時,我們可以將用戶上次 登錄的時間與推送時間做一個比較,如果用戶一年未登陸或幾個月未登錄,我們就不選取其 ID,進(jìn)而避免 無謂的推送。

有的小伙伴可能有疑問:某條通知已經(jīng)被拉取過的話,在其后注冊的用戶是不是不能再接收到這條通知?是的。但如果你想將已拉取過的通知推送給那些后注冊的用戶,也不是特別大的問題。只需要再寫一個定時任務(wù),這個定時任務(wù)可以將通知的 push_time 與用戶的注冊時間比較一下,重新推送即可。

以上就是系統(tǒng)通知的設(shè)計(jì)了,接下來再看看較難的提醒類型的消息。

事件提醒(EventRemind)

之所以稱提醒類型的消息為事件提醒,是因?yàn)榇祟愊⒕峭ㄟ^用戶的行為產(chǎn)生的,如下:

xxx 在某個評論中@了你;

xxx 點(diǎn)贊了你的文章;

xxx 點(diǎn)贊了你的評論;

xxx 回復(fù)了你的文章;

xxx 回復(fù)了你的評論。

諸如此類事件,我們以單詞 action 形容不同的事件(點(diǎn)贊,回復(fù),at)。可以看到除了事件之外,我們還需要了解用戶是在哪個地方產(chǎn)生的事件,以便當(dāng)我們收到提醒時, 點(diǎn)擊這條消息就可以去到事件現(xiàn)場,從而增強(qiáng)用戶體驗(yàn),我以事件源 source 來形容事件發(fā)生的地方。

當(dāng) action 為點(diǎn)贊,source 為文章時,我就知道:有用戶點(diǎn)贊了我的某篇文章;

當(dāng) action 為點(diǎn)贊,source 為評論時,我就知道:有用戶點(diǎn)贊了我的某條評論;

當(dāng) action 為@(at), source 為評論時,我就知道:有用戶在某條評論里@了我;

當(dāng) action 為回復(fù),source 為文章時,我就知道:有用戶回復(fù)了我的某篇文章;

當(dāng) action 為回復(fù),source 為評論時,我就知道:有用戶回復(fù)了我的某條評論;

由此可以設(shè)計(jì)出事件提醒表 t_event_remind,其結(jié)構(gòu)如下:

字段名類型描述event_remind_idLONG消息 IDactionVARCHAR動作類型,如點(diǎn)贊、at(@)、回復(fù)等source_idLONG事件源 ID,如評論 ID、文章 ID 等source_typeVARCHAR事件源類型:"Comment"、"Post"等source_contentVARCHAR事件源的內(nèi)容,比如回復(fù)的內(nèi)容,回復(fù)的評論等等urlVARCHAR事件所發(fā)生的地點(diǎn)鏈接 urlstateBOOLEAN是否已讀sender_idLONG操作者的 ID,即誰關(guān)注了你,at 了你recipient_idLONG接受通知的用戶的 IDremind_timeTIMESTAMP提醒的時間

消息聚合

消息聚合只適用于事件提醒,以聚合之后的點(diǎn)贊消息來說:

100 人 {點(diǎn)贊} 了你的 {文章 ID = 1} :《A》;

100 人 {點(diǎn)贊} 了你的 {文章 ID = 2} :《B》;

100 人 {點(diǎn)贊} 了你的 {評論 ID = 3} :《C》;

聚合之后的消息明顯有兩個特征,即:action 和 source type,這是系統(tǒng)消息和私信都不具備的, 所以我個人認(rèn)為事件提醒的設(shè)計(jì)要稍微比系統(tǒng)消息和私信復(fù)雜。

如何聚合?

稍稍觀察下聚合的消息就可以發(fā)現(xiàn):某一類的聚合消息之間是按照 source type 和 source id 來分組的, 因此我們可以得出以下偽 SQL:

SELECT*FROMt_event_remindWHERErecipient_id=用戶ID

當(dāng)然,SQL 層面的結(jié)果集處理還是很麻煩的,所以我的想法先把用戶所有的點(diǎn)贊消息先查出來, 然后在程序里面進(jìn)行分組,這樣會簡單不少。

拓展

其實(shí)還有一種設(shè)計(jì)提醒表的做法,即按業(yè)務(wù)分類,不同的提醒存入不同的表,這樣可以分為:

點(diǎn)贊提醒表

回復(fù)提醒表

at(@)提醒表。

我認(rèn)為這種設(shè)計(jì)比第一種的更松耦合,不必所有類型的提醒都擠在一張表里,但是這也會帶來表數(shù)量的膨脹。所以各位小伙伴可以自行選擇方案。

私信

站內(nèi)私信一般都是點(diǎn)到點(diǎn)的,且要求是實(shí)時的,服務(wù)端可以采用 Netty 等高性能網(wǎng)絡(luò)通信框架完成請求。我們還是以 b 站為例,看看它是怎么設(shè)計(jì)的:

站內(nèi)消息系統(tǒng)的設(shè)計(jì)

b 站的私信部分可以分為兩部分:

左邊的與不同用戶的聊天室;

與當(dāng)前正在對話的用戶的對話框,顯示了當(dāng)前用戶與目標(biāo)用戶的所有消息。

按照這個設(shè)計(jì),我們可以先設(shè)計(jì)出聊天室表 t_private_chat,因?yàn)槭且粚σ唬粤奶焓冶頃瑢υ挼膬蓚€用戶的信息:

字段名類型描述private_chat_idLONG聊天室 IDuser1_idLONG用戶 1 的 IDuser2_idLONG用戶 2 的 IDlast_messageVARCHAR最后一條消息的內(nèi)容

這里 user1_id 和 user2_id 代表兩個用戶的 ID,并無特定的先后順序。

接下來是私信表 t_private_message 了,私信自然和所屬的聊天室有聯(lián)系,且考慮到私信可以在記錄中刪除(刪除了只是不顯示記錄,但是對方會有記錄,撤回才是真正的刪除),就還需要記錄私信的狀態(tài),以下是我的設(shè)計(jì):

字段名類型描述private_message_idLONG私信 IDcontentTEXT私信內(nèi)容stateBOOLEAN是否已讀sender_removeBOOLEAN發(fā)送消息的人是否把這條消息從聊天記錄中刪除了recipient_removeBOOLEAN接受人是否把這條消息從聊天記錄刪除了sender_idLONG發(fā)送者 IDrecipient_idLONG接受者 IDsend_timeTIMESTAMP發(fā)送時間

消息設(shè)置

消息設(shè)置一般都是針對提醒類型的消息的,且肯定是由用戶自己設(shè)置的。所以我想到一般有以下設(shè)置選項(xiàng):

是否開啟點(diǎn)贊提醒;

是否開啟回復(fù)提醒;

是否開啟@提醒;

下面是 b 站的消息設(shè)置:

消息設(shè)置

可以看到 b 站還添加了陌生人選項(xiàng),也就是說如果給你發(fā)送私信的用戶不是你關(guān)注的用戶,那么視之為陌生人私信,就不接受。

以下是我對于消息設(shè)置的設(shè)計(jì):

字段名類型描述user_idLONG用戶 IDlike_messageBOOLEAN是否接收點(diǎn)贊消息reply_messageBOOLEAN是否接收回復(fù)消息at_messageBOOLEAN是否接收 at 消息stranger_messageBOOLEAN是否接收陌生人的私信

總結(jié)

以上就是我對于整個站內(nèi)消息系統(tǒng)的大概設(shè)計(jì)了,我參考了很多文章的內(nèi)容以及很多網(wǎng)站的設(shè)計(jì),但實(shí)際項(xiàng)目的需求肯定與我所介紹的有很多出入,所以各位小伙伴可以酌情參考。

總結(jié)

以上是生活随笔為你收集整理的以 B 站为例,聊聊站内消息系统的设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。