狀態同步,究竟是推還是拉?

無痕幽雨發表於2018-06-11

出處:狀態同步,究竟是推還是拉?


任何脫離業務的架構設計都是耍流氓。

 

狀態同步,有好友狀態的同步,有群友狀態的同步,有的需要實時同步,有的能夠容忍延時。結合具體場景來看下,狀態同步,究竟是推還是拉。


使用者的線上狀態,分為客戶端狀態(端),服務端狀態(雲)兩種形態。

 

什麼是服務端狀態?

服務端狀態,主要分為線上online和離線offline,不同的狀態,對於不同的業務處理流程可能不同。例如對於訊息的處理:

  • 服務端狀態線上,直接投遞給使用者

  • 服務端狀態離線,直接儲存離線訊息,等使用者下一次登入拉取

 

如何實時更新服務端狀態?

使用者uid-A登入時,會修改使用者的服務端狀態為線上


使用者uid-A登出時,會修改使用者的服務端狀態為離線


經常的,服務端會將使用者的服務端狀態儲存在高可用的快取叢集裡。

 

什麼是客戶端狀態?

不同的產品,會有不同的客戶端狀態,例如隱身、離線、忙碌、勿擾等,這些狀態大多是產品功能需求。有的產品,例如微信,在設計之初,就摒棄了使用者端狀態這個概念。


後文為了方便描述,不妨設待討論的是QQ這種擁有客戶端狀態的產品,並假設客戶端狀態也只有線上和離線兩種狀態,後文統一稱為“使用者狀態”。

 

如何獲取好友的狀態?

uid-A登入時,先去資料庫拉取自己的好友列表,再去快取獲取所有好友的狀態。



使用者uid-A的好友uid-B狀態改變時(由登入、登出等動作觸發),uid-A如何同步這一事件?

這裡就有推拉的設計折衷了。

  • 如果對於狀態變更實時性要求不高,可以採用拉取

uid-A向伺服器輪詢拉取uid-B(其實是自己的全部好友)的狀態,例如每1分鐘一次,其缺點是:

(1)如果uid-B的狀態改變,uid-A獲取不實時,可能有1分鐘時延

(2)如果uid-B的狀態不改變,uid-A會有大量無效的輪詢請求,非常低效

 

  • 如果對於狀態變更實時性要求較高,則必須推送

uid-B狀態改變時(由登入、登出等動作觸發),服務端不僅要在快取中修改uid-B的狀態,還要將這個狀體改變的通知推送給uid-B的線上好友。


推送的優勢是:實時

缺點是:當線上好友量很大時,任何一個使用者狀態的改變,會擴散成N個實時通知,這個N叫做“訊息風暴擴散係數”。假設一個IM系統平均每個使用者有200個好友,平均有20%的好友線上,那麼訊息風暴擴散係數N=40,這意味著,任何一個狀態的變化會變成40個推送請求。

 

群友狀態的一致性,和好友狀態的一致性相比,複雜在哪裡?可不可以採用實時推送?

群這個業務場景大夥也非常之熟悉,你能夠加入若干群(例如20個),假設平均每個群有200人,即你會有4000個群友。

 

理論上群友狀態也可以通過實時推送的方式實現,以保證實時性。進一步討論之前,先一起估算下這個業務場景下的“訊息風暴擴散係數”。

 

假設平均每個使用者加了20個群,平均每個群有200個使用者,依然假設20%的使用者線上,那麼為了保證群友狀態的實時性,每個使用者登入,就要將自己的狀態改變通知傳送給20*200*20%=800個群友,N=800,意味著,任何一個狀態的變化會變成800個推送請求。如果說好友狀態實時推送,訊息風暴擴散係數N=40尚可以接受,那麼群友狀態實時推送,N=800則是災難性的。此類業務往往採用輪詢拉取的方式,獲得群友的狀態。

 

輪詢拉取群友狀態也會給伺服器帶來過大的壓力,還有什麼優化方式?

群友的資料量太大,雖然每個使用者平均加入了20個群,但實際上並不會每次登入都進入每一個群。不採用輪詢拉取,而採用按需拉取,延時拉取的方式,在真正進入一個群時才實時拉取群友的線上狀態,是既能滿足使用者需求(使用者感覺是狀態是實時、一致的,但其實是進入群才拉取的),又能降低伺服器壓力。這是一種常見方法。

 

總結

狀態的實時性與一致性是一個較難解決的技術問題,不同的業務實現方式不同,一般來說

  • 好友狀態同步,是採用推送的方式同步

  • 群友狀態同步,由於訊息風暴擴散係數過大,一般採用拉取的方式同步

  • 群友狀態同步,還能採用按需拉取的優化方式,進一步降低服務端壓力

  • “訊息風暴擴散係數”是指一個訊息發出時,變成N個訊息的擴散係數,這個係數一定程度上決定了技術採用推送還是拉


挖坑篇:《feed流,單聊群聊,系統通知,狀態同步,到底是推還是拉?

填坑篇1:《系統通知,究竟是推還是拉?


相關文章