找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2066

积分

0

好友

294

主题
发表于 2025-12-25 07:47:47 | 查看: 33| 回复: 0

在线人数统计是许多Web应用的基础功能之一,其实现方案多样。本文将介绍一种在实践中常用且高效的方案:基于 Redis 有序集合(Sorted Set,简称zset)来实现。该方案逻辑清晰,性能出色,主要围绕 zaddzrangeByScorezremrangeByScorezrem 四个核心命令展开。

在线人数统计架构示意图

1. 如何判定用户在线状态?

判定逻辑与网站类型密切相关:

  • 需登录的网站:通常根据用户Token(或Session)的有效性来判断。用户登录后,其Token即代表在线状态。
  • 公开网站(无需登录):需要自定义规则来识别唯一用户。常见方式包括IP地址、设备ID(deviceId)、浏览器指纹等,其中推荐使用浏览器指纹方案。
    • 浏览器指纹:通过组合用户代理字符串、屏幕分辨率、时区、安装的插件等信息生成一个唯一标识。可以使用现成的JavaScript库(如FingerprintJS)简化实现。
// 使用FingerprintJS获取访客唯一ID示例
import FingerprintJS from '@fingerprintjs/fingerprintjs';

FingerprintJS.load().then(fp => {
  fp.get().then(result => {
    const visitorId = result.visitorId; // 获取唯一指纹ID
    console.log(visitorId);
    // 可将此ID通过Cookie或Header传递到后端
  });
});

后端服务获取到此唯一ID后,即可将其作为用户的标识符进行后续处理。

2. zadd命令:添加/更新在线用户

命令简介ZADD key score member,向有序集合key中添加一个成员member,其分数为score。若成员已存在,则更新其分数。
应用逻辑:每次用户访问(或心跳)时,将其标识符(如Token或指纹ID)加入集合,并以当前时间戳+预设超时时间作为分数。这确保了同一用户只有最新的登录态被记录。

// 示例:使用Spring Boot环境下的Redis操作
// expireTimeout为预设的令牌超时时间(秒)
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireTimeout);
String expireTimeStr = DateUtil.formatFullTime(expireTime); // 格式化为时间戳字符串
// 添加或更新用户令牌到有序集合 “user.online” 中
redisTemplate.opsForZSet().add("user.online", userToken, Double.parseDouble(expireTimeStr));

3. zrangeByScore命令:查询在线人数

命令简介ZRANGEBYSCORE key min max,返回有序集合key中,分数介于minmax之间的所有成员。
应用逻辑:要查询当前所有在线用户,只需查询分数大于当前时间戳的成员集合,该集合的大小即为在线人数。

// 获取当前时间戳
String now = DateUtil.formatFullTime(LocalDateTime.now());
// 查询分数从当前时间到正无穷(+inf)的所有成员
Set<String> onlineUsers = redisTemplate.opsForZSet().rangeByScore("user.online", Double.parseDouble(now), Double.POSITIVE_INFINITY);
int onlineCount = onlineUsers.size(); // 在线人数

4. zremrangeByScore命令:定时清理离线用户

命令简介ZREMRANGEBYSCORE key min max,移除有序集合key中,分数介于minmax之间的所有成员。
应用逻辑:由于有序集合不会自动移除过期成员,需要设立一个定时任务,定期清理分数小于当前时间戳(即已过期)的成员。

// 定时任务中执行清理
@Scheduled(fixedDelay = 60000) // 例如每分钟执行一次
public void cleanExpiredUsers() {
    String now = DateUtil.formatFullTime(LocalDateTime.now());
    // 移除分数从负无穷(-inf)到当前时间的所有成员(即已过期的用户)
    redisTemplate.opsForZSet().removeRangeByScore("user.online", Double.NEGATIVE_INFINITY, Double.parseDouble(now));
}

5. zrem命令:用户主动退出时移除

命令简介ZREM key member,移除有序集合key中的一个或多个指定成员。
应用逻辑:当用户主动退出登录时,应立即将其从在线集合中移除,确保状态实时准确。

// 用户退出登录时调用
public void logout(String userToken) {
    redisTemplate.opsForZSet().remove("user.online", userToken);
}

方案小结

本方案的核心思路是:创建一个以用户标识为成员(member)、以过期时间戳为分数(score)的Redis有序集合。通过分数范围查询轻松统计在线人数,并借助定时任务与主动删除机制维护集合的清洁。该方案实现巧妙、简单,且能依托 Redis 的高性能支撑高并发场景,是构建在 Spring Boot 等现代Web框架中实现实时在线统计的优选方案。




上一篇:云服务器按流量计费详解:选择场景、优缺点与成本风险分析
下一篇:FinClip实践:小程序一键转iOS、Android与鸿蒙原生App
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-11 20:14 , Processed in 0.261527 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表