方案:利用Redis中zset数据类型进行在线人数统计
Redis中zset数据类型有如下优点:
- Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。
- 不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
- 有序集合的成员是唯一的,但分数(score)却可以重复。
- 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。
一、Redis数据类型
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Set;
@Component
public class RedisUtils {
RedisTemplate<Object, Object> redisTemplate;
public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 添加
*
* @param key 键
* @param value 数据项
* @param score 分数
* @return
*/
public void add(String key, String value, long score) {
redisTemplate.opsForZSet().add(key, value, score);
}
/**
* 获取
*
* @param key 键
* @param start 起始
* @param end 结束
* @return
*/
public Set<Object> rangeByScore(String key, long start, long end) {
return redisTemplate.opsForZSet().rangeByScore(key, start, end);
}
/**
* 统计
*
* @param key 键
* @param start 起始
* @param end 结束
* @return
*/
public Long count(String key, long start, long end) {
return redisTemplate.opsForZSet().count(key, start, end);
}
/**
* 删除
*
* @param key 键
* @param value 数据项
* @return
*/
public Long zrem(String key, String value) {
return redisTemplate.opsForZSet().remove(key, value);
}
}
二、网关设置
private void handlerOnlineUser(String accessToken, Long userId, String uri) {
if (Objects.isNull(userId)) {
return;
}
try {
long t1 = System.currentTimeMillis();
String sessionId = String.format("user-%d", userId);
if (Objects.equals(LOGOUT_URL, uri)) {
redisUtils.zrem(SESSION_KEY, sessionId);
} else {
redisUtils.add(SESSION_KEY, sessionId, t1);
}
log.debug("设置在线用户信息:accessToken={}, sessionId={}, uri={}, cost={}",
accessToken, sessionId, uri, (System.currentTimeMillis() - t1));
} catch (Exception e) {
log.error("设置在线用户信息:accessToken={}, userId={}, uri={}", accessToken, userId, uri, e);
}
}
三、获取统计人数
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Calendar;
import java.util.Date;
@Slf4j
@RestController
@RequestMapping("v1/users/")
public class UserController {
private static final String SESSION_KEY = "jwt-session";
@Autowired
RedisUtils redisUtils;
@Value("${online.access:false}")
private Boolean access;
@ApiOperation(value = "统计在线人员")
@GetMapping("/online/numbers")
@Permission(permissionPublic = true)
public ResponseEntity<Long> countOnlineUsers() {
if (!access) {
throw new AppException("error.api.privilege");
}
Date date = new Date();
long end = date.getTime();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.HOUR, -1);
long start = calendar.getTime().getTime();
log.info("查询在线人员1:start={}, end={}, gap={}", start, end, (end - start) / 1000);
Long members = redisUtils.count(SESSION_KEY, start, end);
log.info("查询在线人员2:start={}, end={}, gap={}, members={}", start, end, (end - start) / 1000, members);
return new ResponseEntity<>(members);
}
}
四、后台redis获取统计人数
登录uat环境
第一步:
[root@cluster-01-k8s-manager ~]# kubectl get pod -n uat
NAME READY STATUS RESTARTS AGE
redis-ha-server-0 2/2 Running 0 5d23h
redis-ha-server-1 2/2 Running 0 5d23h
redis-ha-server-2 2/2 Running 0 5d23h
第二步:
[root@cluster-01-k8s-manager ~]# kubectl exec -it redis-ha-server-1 -n uat /bin/sh
Defaulting container name to redis.
Use 'kubectl describe pod/redis-ha-server-1 -n uat' to see all of the containers in this pod.
/data $
第三步:
/data $ redis-cli
127.0.0.1:6379>
第四步:
127.0.0.1:6379> keys jwt-session*
1) "jwt-session"
第五步:
127.0.0.1:6379> zrangebyscore jwt-session 1618189339902 1618192939902 withscores
1) "\"user-1591\""
2) "1618192532180"
3) "\"user-15632\""
4) "1618192895645"
或者
127.0.0.1:6379> zcount jwt-session 1618189339902 1618192939902
(integer) 1