原理分析
- 用户登录后,每隔一段时间请求接口时,表示当前用户在线。服务器则可以定义一个时间差,例如:5分钟内客户端有请求,视为在线用户
- Redis基于内存进行读写,性能自然比关系型数据库好得多,而且它所提供的Zset可以很方便的构建出一个在线用户的统计服务
- 服务端监控到有接口请求,则更新在线状态
实现代码
ThirdSessionInterceptor拦截器中调用UserOnlineUtil方法
/**
* Copyright (C) 2018-2019
* All rights reserved, Designed By www.joolun.com
* 注意:
* 本软件为www.joolun.com开发研制,未经购买不得使用
* 购买后可获得全部源代码(禁止转卖、分享、上传到码云、github等开源平台)
* 一经发现盗用、分享等行为,将追究法律责任,后果自负
*/
package com.joolun.cloud.mall.api.util;
import cn.hutool.core.util.StrUtil;
import com.joolun.cloud.common.data.tenant.TenantContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* 在线用户统计工具类
* 参考:https://www.jb51.net/article/210809.htm
* @author www.joolun.com
* @date 2022-06-02 14:20:02
*/
@Component
public class UserOnlineUtil {
private static final String ONLINE_USERS = "online_users";
@Resource
private RedisTemplate redisTemplate;
/**
* 添加用户在线信息
* @param clientType 用户端
* @param userId
* @return
*/
public Boolean online(String clientType, String userId) {
return this.redisTemplate.opsForZSet().add(StrUtil.format("{}:{}:{}", TenantContextHolder.getTenantId(), ONLINE_USERS, clientType), userId, Instant.now().toEpochMilli());
}
/**
* 获取一定时间内,在线的用户数量
* @param clientType 用户端
* @param duration
* @return
*/
public Long count(String clientType, Duration duration) {
LocalDateTime now = LocalDateTime.now();
return this.redisTemplate.opsForZSet().count(StrUtil.format("{}:{}:{}", TenantContextHolder.getTenantId(), ONLINE_USERS, clientType),
now.minus(duration).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(),
now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
/**
* 获取所有在线过的用户数量,不论时间
* @param clientType 用户端
* @return
*/
public Long count(String clientType) {
return this.redisTemplate.opsForZSet().zCard(StrUtil.format("{}:{}:{}", TenantContextHolder.getTenantId(), ONLINE_USERS, clientType));
}
/**
* 清除超过一定时间没在线的用户数据
* @param duration
* @return
*/
public Long clear(Duration duration) {
return this.redisTemplate.opsForZSet().removeRangeByScore(StrUtil.format("{}:{}", TenantContextHolder.getTenantId(), ONLINE_USERS), 0,
LocalDateTime.now().minus(duration).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
}
}