一、需求描述
1. 将活跃用户的时间在有序集合中用时间戳储存
2. 淘汰超过1分钟没有活跃的用户
3. 将查询活跃的用户返回
二、代码实现
/**
* 在线观众榜缓存类
* @author Marion
* @date 2021/5/25 10:52
*/
@Service
public class AudienceRankCache {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 淘汰时间(秒)
*/
public static final int rejectTime = 60;
public static class CacheKey {
/**
* stat缓存前缀
*/
public static final String CACHE_PREFIX = "stat:rank:";
/**
* 有资产变化的主播
*/
public static final String ACTIVE_ANCHOR = CACHE_PREFIX + "active:anchor";
public static String getActiveAnchor() {
return ACTIVE_ANCHOR;
}
}
public void addActiveAnchor(long uid, long timestamp) {
String key = CacheKey.getActiveAnchor();
redisTemplate.opsForZSet().add(key, String.valueOf(uid), timestamp);
}
/**
* 查询主播排行榜饭团有变化的主播
*/
public List<Long> getActiveAnchor() {
String key = CacheKey.getActiveAnchor();
Set<String> uidSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
if (uidSet != null && uidSet.size() > 0) {
return uidSet.stream().map(Long::valueOf).collect(Collectors.toList());
}
return new ArrayList<>();
}
/**
* 【定时任务】执行淘汰检测
*/
public void checkRejectedAudience() {
String key = CacheKey.getActiveAnchor();
Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().rangeWithScores(key, 0, -1);
if (tuples != null && tuples.size() > 0) {
// 从最早的时间开始判断,如果超过60s则删除,不超过60s退出循环
long now = System.currentTimeMillis();
// 批量淘汰,如果数据量过多则可以使用pipeline
List<String> removeUid = new ArrayList<>();
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
if (tuple.getScore() == null) {
redisTemplate.opsForZSet().remove(key, tuple.getValue());
}
//如果最早的时间没有过期,则退出循环
if (tuple.getScore() + rejectTime * 1000L > now) {
break;
}
removeUid.add(tuple.getValue());
}
if (removeUid.size() > 0) {
redisTemplate.opsForZSet().remove(key, removeUid);
}
}
}
}
定时任务方法
/**
* 定时任务
* 启动后5秒执行,间隔5秒执行
*/
@Scheduled(initialDelay = 5000, fixedDelay = 5000)
private void checkRejectedAudience() {
audienceRankCache.checkRejectedAudience();
}