系統通知,你以為是推送,其實...
什么是系統通知?
廣義系統通知:
- 有1對1的通知,有一對多的通知;
- 有相對實時的業務通知,有能夠容忍一定延時的系統通知;
任何脫離業務場景的架構設計都是耍流氓,不同業務場景下的系統通知,究竟是推還是拉呢?
第一大類:系統對1的通知
典型業務,計數類通知:
- 有10個美女添加了你為好友;
- 有8個好友私信了你;
很多業務經常有這類計數通知,通知結果只針對你,這類通知是推送,還是拉取的呢?常見的有這樣一些實踐:
如果業務需求對計數需求需要實時展現,例如微博的加好友計數,假如希望實現不刷新網頁,計數就實時變化:
(1) 登錄微博時,會有一個計數的拉取,對網頁端的計數進行初始化:
int getCountByType(int countType)
(2) 在瀏覽微博的過程中,一旦有人加你為好友,服務端對網頁端進行實時推送,告之增加了1個(或者N個)好友:
int addCountByType(int countType, int diff)
這里的思路是,一開始得到初始值,后續推送增量值,由網頁端計算最終計數并呈現最終結果。需要注意,針對不同業務,計數變化的差值可增可減。
上述方案的壞處是,一旦有消息丟失,網頁端的計數會一直不一致,直至再次登錄重新初始化計數。這個計算計數可以優化為在服務器直接計算并通知網頁端最終的結果,網頁端只負責呈現即可,這樣網頁端的邏輯會變輕。
如果業務對此類通知的展現不需要這么實時,完全可以通過拉取:只有在鏈接跳轉,或者刷新網頁時,才重新拉取最新的通知,例如上述計數
int getCountByType(int countType)
這樣系統的實現會最簡單,需要注意,通知拉取要異步,不要影響主頁面的快速返回。
系統對1的推送,例如針對1個用戶的業務計數推送,計數的變化頻率其實非常低,使用cache來存儲這些計數能夠極大提升系統性能。
第二大類:系統對多的通知
系統對多的通知消息,會比系統對1的通知消息復雜一些,以兩個場景為例:
- QQ登錄彈窗新聞;
- QQ右下角彈窗廣告;
先說IM登錄彈窗新聞。
這個通知的需求是:
- 同一天,用戶登錄彈出的新聞是相同的(很多業務符合這樣的場景),不同天新聞則不一樣(但所有用戶都一樣);
- 每天第一次登錄彈出新聞,當天的后續登錄不出新聞;
不妨設有一個表存放彈窗新聞:
t_msg(msg_id, date, msg_content)
有一個表來存放用戶信息:
t_user(user_id, user_info, …)
有一個表來存放用戶收到的新聞彈窗:
t_user_msg(user_id, msg_id, date)
這里的實現明顯不能采用推送的方式:
- 將t_user_msg里對于所有user_id推送插入一個msg_id,表示未讀;
- 在user每天第一次登錄的時候,將當天的msg_id拉取出來,并刪除,表示已讀;
- 在user每天非第一次登錄的時候,就拉取不到msg_id于是不會再次彈窗;
這個笨拙的方式,會導致t_user_msg里有大量的臟數據,畢竟大部分用戶并不會登錄。
如果改為拉取的方式會好很多:
- 在user每天第一次登陸時,將當天的msg_id拉取出來,并插入t_user_msg,表示已讀;
- 在user每天非第一次登陸時,則會插入t_user_msg失敗,則說明已讀,不再進行二次彈窗展現;
這個方式雖然有所優化,但t_user_msg的數據量依然很大。
還有一種巧妙的方式,去除t_user_msg表,改為在t_user表加一列,表示用戶最近拉取的彈窗時間:
t_user(user_id, user_info, last_msg_date, …)
這樣業務流程會升級為:
- 在user每天第一次登錄時,將當天的msg_id拉取出來,并將last_msg_date修改為今天;
- 在user每天非第一次登錄時,發現last_msg_date為今天,則說明今天已讀;
這種方式不再存儲消息與用戶的笛卡爾關系,數據量會大大減少,是不是有點意思?
再說IM右下角彈窗廣告。
這個通知的需求是:
每天會對一批在線用戶推送相同的彈窗TIPS廣告,例如球鞋廣告,手機廣告等;
畫外音:如果1個推送一塊錢,5KW用戶推送收入就有5KW收入喲,一天推個幾次,實現1個億的小目標居然如此簡單。
最直觀的感受,這是一個for循環批量推送的過程。如果是推送,必須要考慮的問題是,推送限速控制,避免短時間內對系統造成沖擊,引發雪崩。
能不能用拉取呢?
完全可以,這是一個對實時性要求不太高的場景,用戶早1分鐘晚1分鐘收到這個廣告影響不大,其實可以借助IM原本已有的keepalive請求,在請求返回時,告之“有消息拉取”,然后采用拉取的方式拉取廣告消息。
這個方案的好處是,由于5KW在線用戶的keepalive請求是均勻的,所以可以很均勻的將廣告拉取的請求同樣均勻的分散到一段時間內,避免5KW集中推送對系統造成沖擊。
簡單總結
廣義系統通知,究竟是推送還是拉取呢?
不同業務,不同需求,實現方式不同。
系統對1的通知:
- 實時性要求高,可以推送;
- 實時性要求低,可以拉取;
系統對N的通知:
- 登錄彈窗新聞,拉取更佳,可以用一個last_msg_date來避免大量數據的存儲;
- 批量彈窗廣告,常見的方法是推送,需要注意限速,也可以拉取,以實現請求的均勻分散;
知其然,知其所以然。
思路比結論更重要。