場景之在線人數或者粉絲查詢實現
直播間在線人數或者粉絲查詢
一、主要功能
通常對於一些實時在線業務中,比如直播業務中的主播,希望讓主播看到直播間實時在線粉絲數等數據,從而從數據方面提升主播的整體直播體驗。
二、簡單方案:
最簡單的方案就是通過所有在線人數判斷與主播是否構成粉絲關係,每個人進入直播間會產生記錄,根據用戶ID去遍歷主播與用戶的關係表,判斷記錄中is_follow關係是否為1,為1用戶則為主播粉絲,記錄下來,遍歷整張表結束則可以統計出在線粉絲人數。
缺點:對於在線粉絲查詢這一功能而言,是相對實時更新並且主播端請求頻率比較高的操作,如果每次查詢根據每個在線的用戶再去掃描表,即使是掃描從庫也是很耗時,因此是不可取的。而對於主播而言短暫的粉絲數量誤差延遲是可以接受的,所以考慮引入redis進行快取記錄。
三、涉及場景
也就是記錄在線粉絲功能中涉及到的主要介面
- 1、用戶進入直播間:用戶進入直播間之後判斷是否粉絲,如果是粉絲並可以添加記錄
- 2、用戶心跳:一般用戶和server,server和主播都會維持等間隔幾秒發送一次心跳,心跳的主要作用包括獲取直播間的一些基礎人數,商品,禮物等資訊以及維持連接正常等功能,同樣基礎數據中也包括在線粉絲數據。而對於用戶端而言,可以在用戶發起心跳的時候,判斷是否是主播粉絲,如果是則添加記錄。
- 3、用戶離開房間:用戶離開直播間觸發判斷是否粉絲,如果是粉絲並從記錄刪除的操作。
- 4、超時斷開連接:一般可能由於一些異常原因,比如網路等問題,用戶和server的連接可能會被判超時,而server的策略則多數是,定期會清理一些超時連接,在清理超時連接的時候,根據連接的用戶是否主播構成粉絲關係,需要從在線粉絲記錄中刪除。
- 5、主播關閉直播間:清除粉絲在線記錄
- 6、主播開播:開啟在線粉絲記錄
- 7、開播過程中添加關註:粉絲在直播間添加關注,這裡根據情況不太需要更新記錄,因為有用戶心跳更新粉絲關注,用戶心跳穩定且頻繁,新增關注帶來的在線粉絲數量上的短暫延遲可以接受。
- 8、開播過程中取消關註:同添加關注
等等……
四、可選方案
利用redis的不同數據結構記錄在線粉絲。
1、採用有序集合
用戶上線時候,判斷構成粉絲關係,則採用ZADD,將用戶以及在線時間添加集合中,其中live_id是直播間id,用主播id或者直播id區分不同的在線粉絲集合。current_timestamp是進入直播間時間戳。
ZADD "online-fans:live_id" <user_id> <current_timestamp>
通過ZCARD命令查看集合中的數量,也就是在線粉絲個數
ZCARD 「online-fans:live_id」
通過ZCOUNT 查看某一時段進入直播間的粉絲。
COUNT "online-fans:live_id" <start_timestamp> <end_timestamp>
2、採用集合
使用有序集合能夠同時儲存粉絲的id以及上線時間戳, 但如果只想要記錄在線的id, 而不想要儲存上線時間, 那麼也可以使用集合來代替有序集合進行記錄。
當進入直播間,判斷是否是粉絲, 執行 SADD 命令將它添加到在線記錄中當中:
SADD "online-fans:live_id" <user_id>
通過使用 SISMEMBER 命令, 可以檢查粉絲是否在直播間:
SISMEMBER "online-fans:live_id" <user_id>
統計在線粉絲數則可以通過執行 SCARD 命令來完成:
SCARD "online-fans:live_id"
與有序集合相同的是,都是集合類型,可以進行一些交集和並集的聚合操作,比如交集判斷連續一周都在直播間的粉絲,並集可以查看一周之內出現在直播間的粉絲等數據。
3、採用Bitmap
使用有序集合或者集合能夠儲存具體的在線用戶名單, 但是卻在粉絲量在線大的時候需要消耗比較多的記憶體;
bitmap相對來說既能夠獲得在線用戶名單, 又可以盡量減少記憶體消耗。Redis 的點陣圖就是一個由二進位位組成的數組, 通過將數組中的每個二進位位與用戶 ID 進行一一對應, 使用點陣圖可以去記錄每個粉絲是否在線。
當一個用戶進入直播間時,判如果是粉絲,使用 SETBIT 命令, 將這個用戶對應的二進位位設置為 1
SETBIT "online-fans:live_id <user_id> 1
通過使用 GETBIT 命令去檢查一個二進位位的值是否為 1 , 判斷粉絲是否在線:
GETBIT "online-fans:live_id" <user_id>
通過 BITCOUNT 命令, 統計出點陣圖中有多少個二進位位被設置成了1,也即是有多少個粉絲直播間在線:
BITCOUNT "online-fans:live_id"
同樣由於,bitmap是用0,1表示對應的粉絲是否在線,也可以多個記錄的bitmap形成與或非運算,計算多個時段或者多個直播間在線的粉絲數。
五、實際方案
綜合實際的情況和要求,採用集合記錄在線粉絲人數。
1、定義redis集合
設置一個集合,具體如下
key = "online-fans:live_id"
value = {user_id1, user_id2, user_id3....}
其中live_id是直播間或者直播場次id,也用主播id定義,集合中記錄用戶的user_id即可
2、修改對應場景下的操作
具體操作包括
- 1,2進入直播間和用戶心跳場景中,通過獲取到用戶的用戶Id,判斷是否與主播構成粉絲關係,如果是,則執行SADD添加集合操作。一般認為主播開播不會超過6h,因此集合有效期設置為為每當有新粉絲進入,則更新快取有效期6h。
SADD "online-fans:live_id" <user_id>
EXPIRE "online-fans:live_id" 6*60*60
- 3,4場景用戶離開直播間與server檢測超時斷開連接,可以直接執行SREM從粉絲集合刪除,由於redis集合移除元素操作的時候,如果元素在集合內則直接移除,不在忽略,因此不需要再判斷粉絲關係。
SREM "online-fans:live_id" <user_id>
- 5場景中,主播主動關閉直播間,則本場直播的實時在線粉絲人數需要清空。刪除對應的直播場次的快取
DEL "online-fans:live_id" <user_id>
UNLINK "online-fans:live_id" <user_id>
具體的redis刪除集合的命令有兩個,一個是del,一個是unlink,具體的是由於redis在執行命令操作的時候是一般是單執行緒的,因此如果是當在線粉絲人數過多導致集合很大的時候,業務流程中執行del操作,會有延遲。因此可以採用單開一個協程或者執行緒去非同步非阻塞執行del操作或者直接使用unlink命令直接返回刪除結果,讓redis單開一個額外的執行緒去執行刪除操作,不阻塞後端流程。
- 6場景開播情況下,不需要主動創建一個空的集合,因為在添加操作的時候如果集合不存在,則會創建。還有點延遲初始化的效果。
- 7,8 直播間內用戶轉變為粉絲的情況可以不考慮,心跳本身也有幾秒一次的短暫定時機制,心跳到達server會更新狀態,數據上的短暫延遲可以接受。