場景之線上人數或者粉絲查詢實現

2022-07-25 06:00:57

直播間線上人數或者粉絲查詢

一、主要功能

通常對於一些實時線上業務中,比如直播業務中的主播,希望讓主播看到直播間實時線上粉絲數等資料,從而從資料方面提升主播的整體直播體驗。

二、簡單方案:

最簡單的方案就是通過所有線上人數判斷與主播是否構成粉絲關係,每個人進入直播間會產生記錄,根據使用者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會更新狀態,資料上的短暫延遲可以接受。