在线人数统计是Web应用中常见的需求,用于实时反映网站的活跃用户数量。实现这一功能有多种方式,本文将介绍一种基于Redis有序集合(ZSet)的经典实现方案,其核心依赖于四个关键命令:zadd、zrangeByScore、zremrangeByScore 和 zrem。

实现步骤
1. 如何认定用户是否在线?
识别用户是否在线主要取决于网站类型:
- 需要登录的网站:通常根据用户的身份令牌(Token)是否有效来判断。
- 公开的、无需登录的网站:则需要创建一套规则来识别独立访客。常见方式有IP地址、设备ID(DeviceId)或浏览器指纹。其中,浏览器指纹是更推荐的方案。
浏览器指纹通过组合多项信息来生成一个相对唯一的访客标识,包括:用户代理字符串(User-Agent)、HTTP请求头、屏幕分辨率、时区、语言设置、浏览器插件列表等。我们可以使用现成的JavaScript库(如FingerprintJS)来简化采集和生成过程。
示例代码如下:
// 安装:npm install @fingerprintjs/fingerprintjs
// 使用示例:
import FingerprintJS from '@fingerprintjs/fingerprintjs';
// 初始化指纹JS库
FingerprintJS.load().then(fp => {
// 获取访客ID
fp.get().then(result => {
const visitorId = result.visitorId;
console.log(visitorId);
});
});
获取到唯一ID后,可将其置于Cookie或请求头中传递给后端,后端便可根据此ID标识用户。
2. ZADD命令:添加在线用户
ZADD命令用于向有序集合中添加成员,并为其指定一个分数(Score)。其基本语法为:ZADD key score member。
在在线人数统计场景中,我们可以将用户令牌(或浏览器指纹ID)作为member,将其过期时间的时间戳作为score。每次用户活动(如登录、访问)时,都执行一次添加操作。
// expireTimeout为用户令牌的过期时长(秒)
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireTimeout);
String expireTimeStr = DateUtil.formatFullTime(expireTime);
// 将用户token添加到名为"user.active"的有序集合中,分数为过期时间戳
redisService.zadd("user.active", Double.parseDouble(expireTimeStr), userToken);
此方案的巧妙之处在于,如果同一用户重复登录,新的记录会以更新的分数(过期时间)覆盖旧记录,从而保证集合中每个用户只保留最新的在线状态。
3. ZRANGEBYSCORE命令:查询在线人数
ZRANGEBYSCORE命令用于获取有序集合中分数在指定区间内的所有成员。语法为:ZRANGEBYSCORE key min max。
要查询当前所有在线用户,只需查询分数大于等于当前时间戳的成员即可。
// 获取当前时间戳
String now = DateUtil.formatFullTime(LocalDateTime.now());
// 查询分数在[now, +inf]区间的所有用户
Set<String> userOnlineStringSet = redisService.zrangeByScore("user.active", now, "+inf");
userOnlineStringSet的大小即为当前的在线人数。
4. ZREMRANGEBYSCORE命令:定时清理过期用户
有序集合不会自动删除过期的成员,因此需要一个定时任务来清理。ZREMRANGEBYSCORE命令可以删除分数在指定区间内的所有成员。语法为:ZREMRANGEBYSCORE key min max。
定时任务可以定期执行以下逻辑,清理已过期的用户记录:
// 获取当前时间戳
String now = DateUtil.formatFullTime(LocalDateTime.now());
// 删除分数在[-inf, now)区间(即已过期)的所有用户
redisService.zremrangeByScore("user.active", "-inf", now);
这个操作是保障数据准确性的关键,防止已离线用户仍被计入统计。此类定时清理任务正是后端服务中常见的维护操作。
5. ZREM命令:用户主动退出时删除
当用户主动退出登录时,应立即将其从在线集合中移除。ZREM命令用于删除指定的一个或多个成员。语法为:ZREM key member [member ...]。
// 用户退出时,直接删除其对应的token记录
redisService.zrem("user.active", userToken);
这样可以确保用户体验的一致性,即主动退出后立即从在线名单中消失。
小结
本方案的核心思路是:创建一个以“在线用户集合”为Key的Redis有序集合,以用户标识为Member,以其会话的过期时间戳为Score。通过ZADD记录或刷新用户在线状态,通过ZRANGEBYSCORE查询在线用户列表,通过ZREMRANGEBYSCORE定时清理过期数据,通过ZREM处理主动退出。方案逻辑清晰,利用Redis有序集合的特性高效实现了在线人数统计功能,在多种业务场景下都具备良好的实用性和扩展性。