SpringCould Gateway counts the number of online users

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

Guess you like

Origin blog.csdn.net/summer_fish/article/details/124950360#comments_25702770