一、問題描述
某個系統的登入介面在被刷。現要建立一個防刷/限流機制,根據登入 IP,30 分鐘之內,只能發起 30 次登入請求。如果超過該限制,則整個 IP 限制登入請求 30 分鐘。
二、設計思路這道題主要是設計兩個 Map:
1、第一個 Map,記錄每個 IP 及其登入的時間。題目要求,30 分鐘之內只能登入 30 次,所以 Map 的 key 為 IP,value 可以設計一個佇列,佇列長度 30,佇列元素為每次的登入時間。
2、第二個 Map,記錄禁止登入的 IP 及禁止開始時間。Map 的 key 為 IP,value 為時間。
三、限流器實現程式碼import java.time.LocalDateTime;import java.util.HashMap;import java.util.LinkedList;public class LimitCacheTool { private static volatile LimitCacheTool instance; // 記錄登入的ip地址及每次登入時間 private static HashMap<String, LinkedList<LocalDateTime>> loginMap = new HashMap<>(); // 記錄禁止登入的ip地址及禁止開始時間 private static HashMap<String, LocalDateTime> forbiddenMap = new HashMap<>(); // 構造器私有化,不能在類的外部隨意建立物件 private LimitCacheTool() { } // 提供一個全域性的訪問點來獲得這個"唯一"的物件 public static LimitCacheTool getInstance() { if (instance == null) { synchronized (LimitCacheTool.class) { if (instance == null) { instance = new LimitCacheTool(); } } } return instance; } public static HashMap<String, LinkedList<LocalDateTime>> getLoginMap() { return loginMap; } public static HashMap<String, LocalDateTime> getForbiddenMap() { return forbiddenMap; } }
四、限流實現邏輯/** * @author:JackRen * @Description:防刷/限流機制 * @date:2021年2月26日上午10:35:18 * @param ip */ public void doLimitLogin(String ip) { String result = ""; LimitCacheTool limitCache = LimitCacheTool.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分鐘"; setRespAttr("msg", result); return; } else { limitCache.getForbiddenMap().clear(); } } // 如果是首次登入,則建立佇列 if (limitCache.getLoginMap().get(ip) == null) { queue = new LinkedList<>(); } 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); setRespAttr("msg", result); return; } } queue.offer(LocalDateTime.now()); limitCache.getLoginMap().put(ip, queue); result = ip + " 登入時間=" + queue.getLast() + " 佇列長度=" + queue.size(); setRespAttr("msg", result); return; }
最新評論