如何優(yōu)雅的實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)功能?
共 4818字,需瀏覽 10分鐘
·
2024-07-18 09:19
往期熱門(mén)文章:
1、JetBrains再出手,這波秀翻了。。
2、FullGC 40次/天到10天1次,真牛B!!
3、不服不行,這才是后端API接口應(yīng)該有的樣子!
4、一個(gè)強(qiáng)大的分布式鎖框架——Lock4j
5、Stream很好,Map很酷,但答應(yīng)我別用toMap()
前言
在線人數(shù)統(tǒng)計(jì)這個(gè)功能相信大家一眼就明白是啥,這個(gè)功能不難做,實(shí)現(xiàn)的方式也很多,這里說(shuō)一下我常使用的方式:使用Redis的有序集合(zset)實(shí)現(xiàn)。核心方法是這四個(gè):zadd、zrangeByScore、zremrangeByScore、zrem。
實(shí)現(xiàn)步驟
1、如何認(rèn)定用戶是否在線?
認(rèn)定用戶在線的條件一般跟網(wǎng)站有關(guān),如果網(wǎng)站需要登錄才能進(jìn)入,那么這種網(wǎng)站就是根據(jù)用戶的token令牌有效性判斷是否在線;如果網(wǎng)站是公開(kāi)的,是那種不需要登錄就可以瀏覽的,那么這種網(wǎng)站一般就需要自定一個(gè)規(guī)則來(lái)識(shí)別用戶,也有很多方式實(shí)現(xiàn)如IP、deviceId、瀏覽器指紋,推薦使用瀏覽器指紋的方式實(shí)現(xiàn)。
瀏覽器指紋可能包括以下信息的組合:用戶代理字符串 (User-Agent string)、HTTP請(qǐng)求頭信息、屏幕分辨率和顏色深度、時(shí)區(qū)和語(yǔ)言設(shè)置、瀏覽器插件詳情等。現(xiàn)成的JavaScript庫(kù),像 FingerprintJS 或 ClientJS,可以幫助簡(jiǎn)化這個(gè)過(guò)程,因?yàn)樗鼈円呀?jīng)實(shí)現(xiàn)了收集上述信息并生成唯一標(biāo)識(shí)的算法。
使用起來(lái)也很簡(jiǎn)單,如下:
// 安裝: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);
});
});
這樣就可以獲取一個(gè)訪問(wèn)公開(kāi)網(wǎng)站的用戶的唯一ID了,當(dāng)用戶訪問(wèn)網(wǎng)站的時(shí)候,將這個(gè)ID放到訪問(wèn)鏈接的Cookie或者h(yuǎn)eader中傳到后臺(tái),后端服務(wù)根據(jù)這個(gè)ID標(biāo)示用戶。
2、zadd命令添加在線用戶
zadd命令介紹 zadd命令有三個(gè)參數(shù)
?key:有序集合的名稱(chēng)。score1、score2 等:分?jǐn)?shù)值,可以是整數(shù)值或雙精度浮點(diǎn)數(shù)。member1、member2 等:要添加到有序集合的成員。例子:
向名為 myzset 的有序集合中添加一個(gè)成員:ZADD myzset 1 "one"
添加在線用戶標(biāo)識(shí)到有序集合中
// expireTime給用戶令牌設(shè)置了一個(gè)過(guò)期時(shí)間
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireTimeout);
String expireTimeStr = DateUtil.formatFullTime(expireTime);
// 添加用戶token到有序集合中
redisService.zadd("user.active", Double.parseDouble(expireTimeStr), userToken);
?由于一個(gè)用戶可能戶會(huì)重復(fù)登錄,這就導(dǎo)致userToken也會(huì)重復(fù),但為了不重復(fù)計(jì)算這個(gè)用戶的訪問(wèn)次數(shù),
zadd命令的第二個(gè)參數(shù)很好的解決了這個(gè)問(wèn)題。我這里的邏輯是:每次添加一個(gè)在線用戶時(shí),利用當(dāng)前時(shí)間加上過(guò)期時(shí)間計(jì)算出一個(gè)分?jǐn)?shù),可以有效保證當(dāng)前用戶只會(huì)存在一個(gè)最新的登錄態(tài)。
3、zrangeByScore命令查詢?cè)诰€人數(shù)
zrangeByScore命令介紹
?key:指定的有序集合的名字。min 和 max:定義了查詢的分?jǐn)?shù)范圍,也可以是 -inf 和 +inf(分別表示“負(fù)無(wú)窮大”和“正無(wú)窮大”)。例子:
查詢分?jǐn)?shù)在 1 到 3之間的所有成員:ZRANGEBYSCORE myzset 1 3
查詢當(dāng)前所有的在線用戶
// 獲取當(dāng)前的日期
String now = DateUtil.formatFullTime(LocalDateTime.now());
// 查詢當(dāng)前日期到"+inf"之間所有的用戶
Set<String> userOnlineStringSet = redisService.zrangeByScore("user.active", now, "+inf");
?利用zrangeByScore方法可以查詢這個(gè)有序集合指定范圍內(nèi)的用戶,這個(gè)
userOnlineStringSet也就是在線用戶集,它的size就是在線人數(shù)了。
4、zremrangeByScore命令定時(shí)清除在線用戶
zremrangeByScore命令介紹
?key:指定的有序集合的名字。min 和 max:定義了查詢的分?jǐn)?shù)范圍,也可以是 -inf 和 +inf(分別表示“負(fù)無(wú)窮大”和“正無(wú)窮大”)。例子:
刪除分?jǐn)?shù)在 1 到 3之間的所有成員:ZREMRANGEBYSCORE myzset 1 3
定時(shí)清除在線用戶
// 獲取當(dāng)前的日期
String now = DateUtil.formatFullTime(LocalDateTime.now());
// 清除當(dāng)前日期到"-inf"之間所有的用戶
redisService.zremrangeByScore(""user.active"","-inf", now);
?由于有序集合不會(huì)自動(dòng)清理下線的用戶,所以這里我們需要寫(xiě)一個(gè)定時(shí)任務(wù)去定時(shí)刪除下線的用戶。
5、zrem命令用戶退出登錄時(shí)刪除成員
zrem命令介紹
?key:指定的有序集合的名字。members:需要?jiǎng)h除的成員 例子:
刪除名為xxx的成員:ZREM myzset "xxx"
定時(shí)清除在線用戶
// 刪除名為xxx的成員
redisService.zrem("user.active", "xxx");
?刪除 zset中的記錄,確保主動(dòng)退出的用戶下線。
小結(jié)一下
這種方案的核心邏輯就是,創(chuàng)建一個(gè)在線用戶身份集合為key,利用用戶身份為member,利用過(guò)期時(shí)間為score,然后對(duì)這個(gè)集合進(jìn)行增刪改查,實(shí)現(xiàn)起來(lái)還是比較巧妙和簡(jiǎn)單的,大家有興趣可以試試看。
文章來(lái)源:https://juejin.cn/post/7356065093060427816
往期熱門(mén)文章:
1、你合并代碼用 merge 還是用 rebase ? 2、99%的時(shí)間里只使用這14個(gè)Git命令就夠了!!! 3、再見(jiàn) Navicat ?不好意思,它免費(fèi)了! 4、@Transactional(readOnly=true)真的是提高性能的靈丹妙藥嗎? 5、8種方案解決重復(fù)提交問(wèn)題! 6、教你用三種方式模擬兩個(gè)線程搶票 7、MySQL中varchar(50)和varchar(500)區(qū)別是什么? 8、頂級(jí)Javaer都在使用的類(lèi)庫(kù),真香! 9、最適合程序員的畫(huà)圖工具? 10、Logback 與 log4j2 性能哪個(gè)更強(qiáng)?
