接口限流處理

1.為什么要限流

當我們設計接口時,需要考慮的因素有很多,其中例如如在設計獲取短信驗證碼的接口時糜芳,第一個想到的就是脐瑰,接口如何去實現(xiàn)訪問控制妖枚,好比如我只能讓你1分鐘之內(nèi)最多請求1次,或者其他規(guī)則蚪黑,這樣很大程度上對接口起到一定的保護盅惜。防止對接口的惡意請求,減少不必要的資源浪費忌穿。當然并非所有接口都需要做這些限制抒寂,這也需要根據(jù)實際業(yè)務而定。

2.怎么限流

基于springboot而言掠剑,我們想到的是通過redis的自加:incr來實現(xiàn)屈芜。我們可以通過用戶的唯一標識來設計成redis的key,值為單位時間內(nèi)用戶的請求次數(shù)。

3.實現(xiàn)

基于朴译,是什么井佑,為什么,怎么做三部曲眠寿,閑話不多說躬翁,直接上代碼。

/**
*  注解用戶訪問控制
 * 在 second 秒內(nèi)盯拱,最大只能請求 maxCount 次
 * @author Json
 * @date 2022/3/9 19:33
 */
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
    /**
     * 通過redis 實現(xiàn)的 這里指定redisKey 否則默認用戶id標識 做實現(xiàn)
     */
    String redisKey();

    /**
     * 時間
     */
    int second() default 1;

    /**
     * 最大請求量
     */
    int maxCount() default 1;

    /**
     * 錯誤文案
     */
    String errorMsg();
}

再寫個攔截器

@Slf4j
@Component
public class RequestLimitIntercept implements HandlerInterceptor {
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {

        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        //HandlerMethod 封裝方法定義相關的信息,如類,方法,參數(shù)等
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 獲取方法中是否包含注解
        RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
        //獲取 類中是否包含注解盒发,也就是controller 是否有注解
        RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
        // 如果 方法上有注解就優(yōu)先選擇方法上的參數(shù),否則類上的參數(shù)
        RequestLimit requestLimit = methodAnnotation != null ? methodAnnotation : classAnnotation;
        if (requestLimit != null) {
            if (isLimit(request, requestLimit)) {
                Result<String> result = new ResultUtil<String>().setErrorMsg(requestLimit.errorMsg());
                response.getWriter().write(JSONObject.toJSON(result).toString());
                return false;
            }
        }

        return true;
    }

    /**
     * 判斷請求是否受限
     *
     * @param request      HttpServletRequest
     * @param requestLimit RequestLimit
     * @return true 允許  false 不允許
     */
    public boolean isLimit(HttpServletRequest request, RequestLimit requestLimit) {
        // 受限的redis 緩存key ,因為這里用瀏覽器做測試狡逢,我就用sessionid 來做唯一key,如果是app ,可以使用 用戶ID 之類的唯一標識宁舰。
        String redisKey = requestLimit.redisKey();
        String limitKey;
        if (StringUtils.isNotBlank(redisKey)) {
            limitKey = redisKey;
        }else {
            limitKey = request.getServletPath() + request.getSession().getId();
        }
        // 從緩存中獲取,當前這個請求訪問了幾次
        Object obj = redisTemplate.opsForValue().get(limitKey);
        if (obj == null) {
            //初始 次數(shù)
            redisTemplate.opsForValue().set(limitKey, "1", requestLimit.second(), TimeUnit.SECONDS);
        } else {
            if (Integer.parseInt(String.valueOf(obj)) >= requestLimit.maxCount()) {
                return true;
            }
            // 次數(shù)自增 +1
            redisTemplate.opsForValue().increment(limitKey);
        }
        return false;
    }

}

再接口上我們加上上面的注解奢浑,即可實現(xiàn)單位時間內(nèi)的訪問控制蛮艰,當然這里只說了大概的原理,可以自行根據(jù)業(yè)務去改造雀彼,當然還有其他辦法去實現(xiàn)類似的效果壤蚜,只是redis做起來會更方便,更好详羡。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仍律,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子实柠,更是在濱河造成了極大的恐慌水泉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異草则,居然都是意外死亡钢拧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門炕横,熙熙樓的掌柜王于貴愁眉苦臉地迎上來源内,“玉大人,你說我怎么就攤上這事份殿∧さ觯” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵卿嘲,是天一觀的道長颂斜。 經(jīng)常有香客問我,道長拾枣,這世上最難降的妖魔是什么沃疮? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮梅肤,結果婚禮上司蔬,老公的妹妹穿的比我還像新娘。我一直安慰自己姨蝴,他們只是感情好俊啼,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著左医,像睡著了一般吨些。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炒辉,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音泉手,去河邊找鬼黔寇。 笑死,一個胖子當著我的面吹牛斩萌,可吹牛的內(nèi)容都是我干的缝裤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼颊郎,長吁一口氣:“原來是場噩夢啊……” “哼憋飞!你這毒婦竟也來了?” 一聲冷哼從身側響起姆吭,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤榛做,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體检眯,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡厘擂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锰瘸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刽严。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖避凝,靈堂內(nèi)的尸體忽然破棺而出舞萄,到底是詐尸還是另有隱情,我是刑警寧澤管削,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布倒脓,位于F島的核電站,受9級特大地震影響佩谣,放射性物質(zhì)發(fā)生泄漏把还。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一茸俭、第九天 我趴在偏房一處隱蔽的房頂上張望吊履。 院中可真熱鬧,春花似錦调鬓、人聲如沸艇炎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缀踪。三九已至,卻和暖如春虹脯,著一層夾襖步出監(jiān)牢的瞬間驴娃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工循集, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留唇敞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓咒彤,卻偏偏與公主長得像疆柔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子镶柱,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355