一个利用redis zset统计在线用户数的方法.
By:Roy.LiuLast updated:2021-08-15
对于物联网设备,现在的应用程序倾向于使用Heartbeat来识别用户是否在线。用户登录后,每隔一段时间向服务器推送一条消息,表示当前用户在线。服务端可以定义一个时间差,例如:如果在5分钟内收到客户端的心跳消息,则视为在线用户。
一. 用数据库实现
这是一种比较偷懒的方法,服务端每次收到心跳,就更新数据库表的记录,特别是最后更新时间字段。当查询有多少设备在线的时候,只需要用SQL 语句筛选出最后更新时间在最近5分钟时间之内即可。但这种方式,不是很好的方式,数据库表更新频繁,效率低下。因为心跳导致索引经常在更新。
二. 用Redis zset实现
这是一个比较理想的实现方式,redis基于内存读写,性能自然比关系型数据库好很多,而且它提供的Zset为在线用户搭建统计服务非常方便。
zset 里面有一个score,表示权重,用它来存储心跳的时间。那么判断用户设备是否在线,完全可以通过查询zset里面是否有这个记录,并用score与当前时间对比,在心跳间隔范围内,那么就当做在线的。如果要查询所有的在线设备列表,完全可以通过zset zrange 命令获取在一定区间内的score的成员列表。而且还可以做到分页查询。
zset 基本用法:
添加元素
ZADD key score member [score member ...]
一次向集合中添加一个或多个元素,如果member已经存在,则当前score 进行叠加
计算所有元素 ZCARD key
统计分值在 min 和 max 之间的元素个数
ZCOUNT key min max
删除分值在 min 和 max 之间的元素
ZREMRANGEBYSCORE key min max
利用zset提供的方法,自己写一个类:
@Component public class OnlineUserStatsService { private static final String ONLINE_USERS = "onlie_users"; @Resource private StringRedisTemplate stringRedisTemplate; // 增加在线用户 public Boolean online(Integer userId) { return this.stringRedisTemplate.opsForZSet().add(ONLINE_USERS, userId.toString(), Instant.now().toEpochMilli()); } // 特定时间范围内的数量 public Long count(Duration duration) { LocalDateTime now = LocalDateTime.now(); return this.stringRedisTemplate.opsForZSet().count(ONLINE_USERS, now.minus(duration).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(), now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } // 总数量,与时间无关 public Long count() { return this.stringRedisTemplate.opsForZSet().zCard(ONLINE_USERS); } // 清除时间范围内的数据。 public Long clear(Duration duration) { return this.stringRedisTemplate.opsForZSet().removeRangeByScore(ONLINE_USERS, 0, LocalDateTime.now().minus(duration).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } }
调用方法就可以实现统计在线用户总数,分页,删除等操作。
另外为了防止zset里面数据过多,可以在每天晚上空闲时间,删除过期的数据。做一个定时任务来操作。
From:一号门
Previous:springboot 读取资源文件
COMMENTS