Solution: Use the zset data type in Redis to count the number of people online
The zset data type in Redis has the following advantages:
- Redis ordered collections, like collections, are also collections of string type elements, and duplicate members are not allowed.
- The difference is that each element will be associated with a score of type double. Redis uses scores to sort the members of the set from small to large.
- The members of the ordered set are unique, but the score (score) can be repeated.
- The collection is implemented through a hash table, so the complexity of adding, deleting, and searching is O(1). The maximum number of members in a set is 2^32 - 1 (4294967295, each set can store more than 4 billion members).
1. Redis data type
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);
}
}
2. Gateway settings
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);
}
}
3. Get statistics
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);
}
}
Fourth, the background redis obtains the statistics of the number of people
登录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