经典笔试题:设计一个限流系统

笔试题:

登陆安全的题目,如果你的系统登陆接口在被刷。我们要建立一个防刷系统。

根据登陆ip,30分钟之内,只能请求30次登陆请求,如果超过这个限制,则整个ip限制登陆请求30分钟

设计数据结构和实现代码模拟分布式限流,多线程问题。不允许使用redis等。

 

设计思路:

这道题主要是设计两个Map,

第一个Map,记录每个IP及其登录的时间,题目要求,30分钟之内只能登录30次,

所以Map的key为IP,value可以设计一个队列,队列长度30,队列元素为每次的登录时间。

第二个Map,记录禁止登录的IP及禁止开始时间,Map的key为IP,value为时间。

 

实现代码如下:

/**
 * 限流器实现代码
 * 单例模式是为了保证一个JVM中只有一个限流器
 */
public class LimitCache {

    private static LimitCache instance;

    // 记录登录的ip地址及每次登录时间
    private static HashMap<String, LinkedList<LocalDateTime>> loginMap = new HashMap<String, LinkedList<LocalDateTime>>();

    // 记录禁止登录的ip地址及禁止开始时间
    private static HashMap<String, LocalDateTime> forbiddenMap = new HashMap<String, LocalDateTime>();

    // 私有化构造方法
    private LimitCache() {

    }

    // 单例:双重检查模式 (DCL)
    public static synchronized LimitCache getInstance() {
        if (instance == null) {
            synchronized (LimitCache.class) {
                if (instance == null) {
                    instance = new LimitCache();
                }
            }
        }
        return instance;
    }

    public static HashMap<String, LinkedList<LocalDateTime>> getLoginMap() {
        return loginMap;
    }

    public static HashMap<String, LocalDateTime> getForbiddenMap() {
        return forbiddenMap;
    }

}

 

/**
 * 模拟登陆
 *
 */
@RestController
public class LoginController {

    @GetMapping("/login")
    public String login(String ip) {

        String result = "";
        LimitCache limitCache = LimitCache.getInstance();
        LinkedList<LocalDateTime> queue = null;

        // 先判断ip地址是否禁止登录
        LocalDateTime forbiddenTime = limitCache.getForbiddenMap().get(ip);
        if (forbiddenTime != null) {
            Long after = ChronoUnit.MINUTES.between(forbiddenTime, LocalDateTime.now());
            if (after <= 30) {
                result = "当前时间=" + LocalDateTime.now() + " 上次禁止登录时间= " + forbiddenTime + " 距上次被禁时间没有超过30分钟";
                return result;
            } else {
                limitCache.getForbiddenMap().clear();
            }
        }

        // 如果是首次登录,则创建队列
        if (limitCache.getLoginMap().get(ip) == null) {
            queue = new LinkedList<LocalDateTime>();
        } else {
            queue = limitCache.getLoginMap().get(ip);
        }

        // 登录次数达到登录次数上限
        if (queue.size() == 30) {
            // 当前时间和队列中最早的登录时间比较 是否小于30分钟
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime firstLoginTime = queue.poll();
            Long duration = ChronoUnit.MINUTES.between(firstLoginTime, now);
            if (duration <= 30) {
                result = "30分钟内登录超过30次,不允许登录,30分钟后再登录";
                // 禁止该IP登录
                limitCache.getLoginMap().clear();
                limitCache.getForbiddenMap().put(ip, now);
                return result;
            } 
        } 
        
     queue.offer(LocalDateTime.now()); limitCache.getLoginMap().put(ip, queue); result
= ip + " 登录时间=" + queue.getLast() + " 队列长度=" + queue.size(); return result; } }

 

posted @ 2020-10-29 15:47  gaopengpy  阅读(1097)  评论(0编辑  收藏  举报