當前位置: 妍妍網 > 碼農

如何優雅的實作線上人數統計功能?

2024-05-25碼農

來源:juejin.cn/post/7356065093060427816

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了261小節,累計41w+字,講解圖:1806張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,

一、前言

線上人數統計這個功能相信大家一眼就明白是啥,這個功能不難做,實作的方式也很多,這裏說一下我常使用的方式:使用Redis的有序集合(zset)實作。

核心方法是這四個: zadd zrangeByScore zremrangeByScore zrem

圖片

二、實作步驟

1. 如何認定使用者是否線上?

認定使用者線上的條件一般跟網站有關,如果網站需要登入才能進入,那麽這種網站就是根據使用者的token令牌有效性判斷是否線上;

如果網站是公開的,是那種不需要登入就可以瀏覽的,那麽這種網站一般就需要自定一個規則來辨識使用者,也有很多方式實作如IP、deviceId、瀏覽器指紋,推薦使用瀏覽器指紋的方式實作。

瀏覽器指紋可能包括以下資訊的組合:使用者代理字串 ( User-Agent string )、HTTP請求頭資訊、螢幕分辨率和顏色深度、時區和語言設定、瀏覽器外掛程式詳情等。現成的JavaScript庫,像 FingerprintJS ClientJS ,可以幫助簡化這個過程,因為它們已經實作了收集上述資訊並生成唯一標識的演算法。

使用起來也很簡單,如下:

// 安裝:npm install @fingerprintjs/fingerprintjs
// 使用範例:
import FingerprintJS from '@fingerprintjs/fingerprintjs';
// 初始化指紋JS Library
FingerprintJS.load().then(fp => {
// 獲取訪客ID
fp.get().then(result => {
const visitorId = result.visitorId;
console.log(visitorId);
});
});

這樣就可以獲取一個存取公開網站的使用者的唯一ID了,當使用者存取網站的時候,將這個ID放到存取連結的Cookie或者header中傳到後台,後端服務根據這個ID標示使用者。

2. zadd命令添加線上使用者

1)zadd命令介紹

zadd命令有三個參數

  • key:有序集合的名稱。

  • score1、score2 等:分數值,可以是整數值或雙精度浮點數。

  • member1、member2 等:要添加到有序集合的成員。

  • 例子:向名為 myzset 的有序集合中添加一個成員: ZADD myzset 1 "one"

    2)添加線上使用者標識到有序集合中

    // expireTime給使用者令牌設定了一個過期時間
    LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireTimeout);
    String expireTimeStr = DateUtil.formatFullTime(expireTime);
    // 添加使用者token到有序集合中
    redisService.zadd("user.active", Double.parseDouble(expireTimeStr), userToken);

    由於一個使用者可能戶會重復登入,這就導致userToken也會重復,但為了不重復計算這個使用者的存取次數,zadd命令的第二個參數很好的解決了這個問題。

    我這裏的邏輯是:每次添加一個線上使用者時,利用當前時間加上過期時間計算出一個分數,可以有效保證當前使用者只會存在一個最新的登入態。

    3. zrangeByScore命令查詢線上人數

    1)zrangeByScore命令介紹

  • key:指定的有序集合的名字。

  • min 和 max:定義了查詢的分數範圍,也可以是 -inf 和 +inf(分別表示「負無窮大」和「正無窮大」)。

  • 例子:查詢分數在 1 到 3之間的所有成員: ZRANGEBYSCORE myzset 1 3

    2)查詢當前所有的線上使用者

    // 獲取當前的日期
    String now = DateUtil.formatFullTime(LocalDateTime.now());
    // 查詢當前日期到"+inf"之間所有的使用者
    Set<String> userOnlineStringSet = redisService.zrangeByScore("user.active", now, "+inf");

    利用 zrangeByScore 方法可以查詢這個有序集合指定範圍內的使用者,這個 userOnlineStringSet 也就是線上使用者集,它的size就是線上人數了。

    4. zremrangeByScore命令定時清除線上使用者

    1)zremrangeByScore命令介紹

  • key:指定的有序集合的名字。

  • min 和 max:定義了查詢的分數範圍,也可以是 -inf 和 +inf(分別表示「負無窮大」和「正無窮大」)。

  • 例子:刪除分數在 1 到 3之間的所有成員: ZREMRANGEBYSCORE myzset 1 3

    2)定時清除線上使用者

    // 獲取當前的日期
    String now = DateUtil.formatFullTime(LocalDateTime.now());
    // 清除當前日期到"-inf"之間所有的使用者
    redisService.zremrangeByScore(""user.active"","-inf", now);

    由於有序集合不會自動清理下線的使用者,所以這裏我們需要寫一個定時任務去定時刪除下線的使用者。

    5. zrem命令使用者結束登入時刪除成員

    1)zrem命令介紹

  • key:指定的有序集合的名字。

  • members:需要刪除的成員

  • 例子:刪除名為xxx的成員: ZREM myzset "xxx"

    2)定時清除線上使用者

    // 刪除名為xxx的成員
    redisService.zrem("user.active""xxx");

    刪除 zset中的記錄,確保主動結束的使用者下線。

    三、小結一下

    這種方案的核心邏輯就是,建立一個線上使用者身份集合為key,利用使用者身份為member,利用過期時間為score,然後對這個集合進行增刪改查,實作起來還是比較巧妙和簡單的,大家有興趣可以試試看。

    👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

    新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了261小節,累計41w+字,講解圖:1806張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,


    1. 

    2. 

    3. 

    4. 

    最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。

    獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。

    PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下在看,加個星標,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。

    「在看」支持小哈呀,謝謝啦