SpringCould Gateway统计在线人数

方案:利用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

猜你喜欢

转载自blog.csdn.net/summer_fish/article/details/124950360#comments_25702770