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

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中,分数介于min和max之间的所有成员。
应用逻辑:要查询当前所有在线用户,只需查询分数大于当前时间戳的成员集合,该集合的大小即为在线人数。
// 获取当前时间戳
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中,分数介于min和max之间的所有成员。
应用逻辑:由于有序集合不会自动移除过期成员,需要设立一个定时任务,定期清理分数小于当前时间戳(即已过期)的成员。
// 定时任务中执行清理
@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框架中实现实时在线统计的优选方案。