一、前言
大家好!我是sum墨,一個一線的底層碼農,平時喜歡研究和思考一些技術相關的問題並整理成文,限於本人水平,如果文章和程式碼有表述不當之處,還請不吝賜教。
線上人數統計這個功能相信大家一眼就明白是啥,這個功能不難做,實現的方式也很多,這裡說一下我常使用的方式:使用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,然後對這個集合進行增刪改查,實現起來還是比較巧妙和簡單的,大家有興趣可以試試看。