站內消息設計與實現

http


0x01.About

最近在處理系統消息模塊,查閱了不少實踐案例,各有針對性。html

首先站內消息主要包括:我的消息(評論,點贊),系統消息,訂閱消息,私信。git

其中,訂閱區分用戶羣,即系統消息是一個特殊的全部人訂閱的訂閱消息,特色是一對多。github

前三個實時性比較低,最後一個實時性高,離線狀態下是私信,若是雙方在線要轉爲聊天室,特色是一對一。web

那麼,接下來,該選個方案了,SQL or NoSQL?redis



0x02.Mysql實現

首先,對於我的消息、私信("UserMessage"),一條消息插一句,Mysql跑跑沒問題。sql

對於系統消息或訂閱消息,必然不能夠,假若有10萬用戶,一次性那麼要插入10萬條消息,Mysql必死。數據庫

那麼就是說,要設立一個系統庫("SystemMessage"),每當用戶登陸,就去跑跑系統庫("SystemMessage"),把未讀的系統庫跑到我的庫。json

關於訂閱消息就比較麻煩了,對用戶分組?對消息分組?緩存

關係型數據庫處理集合問題是比較麻煩的,目前想到的結論是創建一個表("RssMessage")存儲消息類型,消息索引。數據結構

下面列了大體的數據庫模型:

Mysql

看完這個數據庫設計,我也以爲好難受,吐槽前先來想一想爲何吧。

UserSystemRelation表用於記錄用戶讀取到哪一個位置的標記。

能夠看到,UserMessage與SystemMessage表中,title、tid、ctime、type字段冗餘了,好像也不必,

可是從用戶功能上看,當用戶登錄後,查找本身站內消息,必然要用到的有:status,必然要顯示的有:title、ctime,type做爲用戶進入消息面板後,要篩選的方式之一,這樣的話,Mysql就只要跑一個表就能夠完成顯示給用戶的最新站內消息了。

因爲MessageText多是一個大信息通知,用戶查看我的消息時候,並未查看MessageText內容,因此單獨放一張表。

相應處理流程

  • 用戶登陸後,先經過"UserSystemRelation"表查詢是否有新的系統消息
  • 若是"UserSystemRelation",查找到自身uid,同步系統消息到我的消息;若是"UserSystemRelation"未查找到自身uid,直接插入"UserSystemRelation",並讀取最近50條系統消息。
  • 用戶點擊未讀消息,獲取"MessageText"",並更新狀態(status)爲已讀。
  • 用戶經過"status"、"type",能夠篩選系統消息。



0x03.Mysql+MongoDB實現

因爲Mongodb是一種文檔型的數據結構,因此,能夠考慮把全部數據轉成json直接塞給Mongodb。

基於用戶的習慣,讀多寫少,大部分時候都是看到消息,刪除、更新比較少,若是數據沒更新直接讀Mongodb,若是數據更新,直接刪除Mongodb
的索引。

這個考慮是在,用戶數量很大的時候,要在"UserSystem"表裏查找到用戶消息比較慢的時候用,相似於吧Mongodb當緩存。



0x04.Redis實現

看了Mysql下站內消息的數據庫設計,我也以爲很蛋疼,臨時過渡沒事,可是仍是NoSQL合適。

Redis自帶訂閱與發佈系統,http://redisbook.readthedocs.org/en/latest/feature/pubsub.html

在下圖展現的這個 pubsub_channels 示例中, client2 、 client5 和 client1 就訂閱了 channel1 , 而其餘頻道也分別被別的客戶端所訂閱:

訂閱

只要是訂閱了相應地頻道,就會收到頻道的消息。

把用戶ID做爲頻道,私信就是反向的頻道訂閱,系統消息就是全部用戶的訂閱,那麼離線的消息呢?

一、線上用戶

仍是存在系統或我的的哈希表裏,等上線後再去讀取。

在Python中,訂閱發佈消息(Publish)以下:

import redis,time
queue = redis.StrictRedis(host='localhost', port=6379, db=0)
channel = queue.pubsub()

for i in range(100): 
    queue.publish("test", i)
    time.sleep(0.1)

Python中,訂閱監聽消息(Subcribe)以下:

import redis,time
r = redis.StrictRedis(host='localhost', port=6379, db=0)
p = r.pubsub()
p.subscribe('test')

while True:
    message = p.get_message()
    if message:
        print "Subscriber: %s" % message['data']

Redis-py的API能夠看GitHub:https://github.com/andymccurdy/redis-py

這是線上用戶作法。

二、線下用戶

看過一種作法是創建一個Redis鏈表,存儲登錄用戶,當用戶登錄就直接發送,沒登錄就暫存起來。

這裏的話,能夠用WebSocket實時監聽,按期發送心跳包,若是在線直接返回Redis自帶的訂閱系統。

系統消息創建一個集合:

SADD system:2015-08-03 7 8 9 10 11

第一段標示系統信息,第二段標示日期,後面的數字標示message id。

我的消息創建一個集合:

SADD user:12345:read 1 2 3 4

第一段標示用戶信息集合,第二段標示用戶id,下一段標示消息類型爲已讀,後面的數字標示message id。

關於訂閱消息以下:

SADD rss:xiaocao 12 13 14 15

那麼你就收到小草的訂閱消息,消息ID分別是 12, 13, 14, 15

還有很重要的消息數據存儲,

HMSET message:12 title 標題 content 內容 date 2015-08-03

Python建立數據庫的例子就是:

import redis,time,threading,random
pool = redis.ConnectionPool(host='localhost', port=6379, db=1)
rs = redis.Redis(connection_pool=pool)

rs.sadd("user:123:read", "1", "2")
rs.sadd("user:123:unread", "4", "5", "6")
rs.sadd("system:2015-08-03", "7", "8", "9", "10", "11")
rs.sadd("rss:xiaocao", "12", "13", "14", "15", "11")

for i in range(15):
    rs.hset("message:"+str(i), "title", "title=>"+str(random.uniform(1, 99999)))
    rs.hset("message:"+str(i), "content","content=>"+str(time.time()))
    rs.hset("message:"+str(i), "date", str(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())))


參考:



本文出自 夏日小草,轉載請註明出處:http://homeway.me/2015/08/03/website-system-message/

-by小草

2015-08-03 01:35:10

相關文章
相關標籤/搜索