最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Redis實(shí)現(xiàn)API接口訪問次數(shù)限制

 更新時間:2024年11月12日 10:58:33   作者:東皋長歌  
日常開發(fā)中會有一個常見的需求,需要限制接口在單位時間內(nèi)的訪問次數(shù),比如說某個免費(fèi)的接口限制單個IP一分鐘內(nèi)只能訪問5次,該怎么實(shí)現(xiàn)呢,本文小編給大家介紹了如何基于Redis實(shí)現(xiàn)API接口訪問次數(shù)限制,需要的朋友可以參考下

一,概述

日常開發(fā)中會有一個常見的需求,需要限制接口在單位時間內(nèi)的訪問次數(shù),比如說某個免費(fèi)的接口限制單個IP一分鐘內(nèi)只能訪問5次。該怎么實(shí)現(xiàn)呢,通常大家都會想到用redis,確實(shí)通過redis可以實(shí)現(xiàn)這個功能,下面實(shí)現(xiàn)一下。

二,常見錯誤

固定時間窗口

有人設(shè)計了一個在每分鐘內(nèi)只允許訪問1000次的限流方案,如下圖01:00s-02:00s之間只允許訪問1000次。這種設(shè)計的問題在于,請求可能在01:59s-02:00s之間被請求1000次,02:00s-02:01s之間被請求了1000次,這種情況下01:59s-02:01s間隔0.02s之間被請求2000次,很顯然這種設(shè)計是錯誤的。

三, 實(shí)現(xiàn)

1,基于滑動時間窗口

在指定的時間窗口內(nèi)次數(shù)是累積的,超過閾值,都會限制。

2,流程如下

3,代碼實(shí)現(xiàn)

前提:pom文件引入redis,Spring AOP等

(1)添加注解RequestLimit

package com.xxx.demo.aspect;
 
import java.lang.annotation.*;
 
 
/**
 * 接口訪問頻率注解,默認(rèn)一分鐘只能訪問10次
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
    // 限制時間 單位:秒(默認(rèn)值:一分鐘)
    long period() default 60;
    // 允許請求的次數(shù)(默認(rèn)值:10次)
    long count() default 10;
}

(2)添加切面實(shí)現(xiàn)注解的限制訪問邏輯

package com.xxx.demo.aspect;
 
import com.xgd.demo.commons.ErrorCode;
import com.xgd.demo.handler.BusinessException;
import com.xgd.demo.util.IpUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.concurrent.TimeUnit;
 
/**
 * @date 2024/11/8 上午8:43
 */
@Aspect
@Component
@Log4j2
public class RequestLimitAspect {
    @Autowired
    RedisTemplate redisTemplate;
 
    @Pointcut("@annotation(requestLimit)")
    public void controllerAspect(RequestLimit requestLimit) {}
 
    @Around("controllerAspect(requestLimit)")
    public Object doAround(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {
        // 從注解中獲取限制次數(shù)和窗口時間
        long period = requestLimit.period();
        long limitCount = requestLimit.count();
 
        // 請求
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String ip = IpUtil.getIpFromRequest(request);
        String uri = request.getRequestURI();
        //設(shè)置客戶端訪問的key
        String key = "req_limit_".concat(uri).concat(ip);
 
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        // 添加當(dāng)前時間戳,分?jǐn)?shù)為當(dāng)前時間戳
        long currentMs = System.currentTimeMillis();
        zSetOperations.add(key, currentMs, currentMs);
        // 設(shè)置窗口時間作為過期時間
        redisTemplate.expire(key, period, TimeUnit.SECONDS);
        // 移除掉不在窗口里的數(shù)據(jù)
        zSetOperations.removeRangeByScore(key, 0, currentMs - period * 1000);
        // 查詢窗口內(nèi)已經(jīng)訪問過的次數(shù)
        Long count = zSetOperations.zCard(key);
        if (count > limitCount) {
            log.error("接口攔截:{} 請求超過限制頻率【{}次/{}s】,IP為{}", uri, limitCount, period, ip);
            throw new BusinessException(ErrorCode.REQUEST_LIMITED.getCode(), ErrorCode.REQUEST_LIMITED.getMessage());
        }
 
        // 繼續(xù)執(zhí)行請求
        return  joinPoint.proceed();
    }
}

上面里面請求被攔截,是拋出了一個自定義的業(yè)務(wù)異常,大家可以根據(jù)自己的情況自己定義。

(3)同時附上上面中引用到自定義工具類

package com.xxx.demo.util;
 
import jakarta.servlet.http.HttpServletRequest;
import java.util.Objects;
 
/**
 * @date 2024/11/8 上午9:06
 */
public class IpUtil {
    private static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For";
    private static final String X_REAL_IP_HEADER = "X-Real-IP";
 
    /**
     * 從請求中獲取IP
     *
     * @return IP;當(dāng)獲取不到時,返回null
     */
    public static String getIpFromRequest(HttpServletRequest request ) {
        return getRealIp(request);
    }
 
    /**
     * 獲取請求的真實(shí)IP,優(yōu)先級從高到低為:<br/>
     * 1.從請求頭X-Forwarded-For中獲取ip,并且只獲取第一個ip(從左到右) <br/>
     * 2.從請求頭X-Real-IP中獲取ip <br/>
     * 3.使用{@link HttpServletRequest#getRemoteAddr()}方法獲取ip
     *
     * @param request 請求對象,必須不能為null
     * @return ip
     */
    private static String getRealIp(HttpServletRequest request) {
        Objects.requireNonNull(request, "request must be not null");
 
        String ip = request.getHeader(X_FORWARDED_FOR_HEADER);
        if (ip != null && !ip.isBlank()) {
            int delimiterIndex = ip.indexOf(',');
            if (delimiterIndex != -1) {
                // 如果存在多個ip,則取第一個ip
                ip = ip.substring(0, delimiterIndex);
            }
 
            return ip;
        }
 
        ip = request.getHeader(X_REAL_IP_HEADER);
        if (ip != null && !ip.isBlank()) {
            return ip;
        } else {
            return request.getRemoteAddr();
        }
    }
}

(4)使用注解

這里限制為10秒內(nèi)只允許訪問3次,超過就拋出異常

(5)訪問測試

前3次訪問,接口正常訪問

后面的訪問,返回自定義異常的結(jié)果

到此這篇關(guān)于基于Redis實(shí)現(xiàn)API接口訪問次數(shù)限制的文章就介紹到這了,更多相關(guān)Redis API接口訪問限制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis哈希Hash鍵值對集合操作(查詢增加修改)

    Redis哈希Hash鍵值對集合操作(查詢增加修改)

    Redis中的Hash數(shù)據(jù)?是一個?鍵值對集合,本文主要介紹了Redis哈希Hash鍵值對集合操作(查詢增加修改),具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • redis數(shù)據(jù)結(jié)構(gòu)之壓縮列表

    redis數(shù)據(jù)結(jié)構(gòu)之壓縮列表

    這篇文章主要介紹了redis數(shù)據(jù)結(jié)構(gòu)之壓縮列表,壓縮列表是列表list和hash數(shù)據(jù)結(jié)構(gòu)的底層實(shí)現(xiàn)之一,是redis為了節(jié)約內(nèi)存而開發(fā)的,由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型數(shù)據(jù)結(jié)構(gòu),下面詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-03-03
  • Redis?HyperLogLog數(shù)據(jù)量統(tǒng)計的實(shí)現(xiàn)實(shí)例

    Redis?HyperLogLog數(shù)據(jù)量統(tǒng)計的實(shí)現(xiàn)實(shí)例

    在大數(shù)據(jù)時代,統(tǒng)計海量數(shù)據(jù)中的唯一值是一個常見的需求,但同時也是極具挑戰(zhàn)性的任務(wù),傳統(tǒng)的統(tǒng)計方法可能會消耗大量內(nèi)存或計算資源,而?Redis?的?HyperLogLog?數(shù)據(jù)結(jié)構(gòu)?則提供了一種高效、輕量的解決方案,下面就來詳細(xì)介紹一下HyperLogLog的使用,感興趣的可以了解一下
    2025-09-09
  • 5分鐘搭建redis集群(redis5.0.5)

    5分鐘搭建redis集群(redis5.0.5)

    本文主要介紹了5分鐘搭建redis集群,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 在ssm項(xiàng)目中使用redis緩存查詢數(shù)據(jù)的方法

    在ssm項(xiàng)目中使用redis緩存查詢數(shù)據(jù)的方法

    本文主要簡單的使用Java代碼進(jìn)行redis緩存,即在查詢的時候先在service層從redis緩存中獲取數(shù)據(jù)。如果大家對在ssm項(xiàng)目中使用redis緩存查詢數(shù)據(jù)的相關(guān)知識感興趣的朋友跟隨腳本之家小編一起看看吧
    2018-03-03
  • Redis分片集群的實(shí)現(xiàn)示例

    Redis分片集群的實(shí)現(xiàn)示例

    本文介紹了搭建Redis分片集群,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • mac下設(shè)置redis開機(jī)啟動方法步驟

    mac下設(shè)置redis開機(jī)啟動方法步驟

    這篇文章主要介紹了mac下設(shè)置redis開機(jī)啟動,本文詳細(xì)的給出了操作步驟,需要的朋友可以參考下
    2015-07-07
  • Redis slowlog使用和實(shí)現(xiàn)

    Redis slowlog使用和實(shí)現(xiàn)

    slowlog是redis server記錄查詢命令執(zhí)行時間的日志模塊, 本文就來介紹一下slowlog使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-02-02
  • Redis RDB技術(shù)底層原理詳解

    Redis RDB技術(shù)底層原理詳解

    為了使Redis在重啟之后仍能保證數(shù)據(jù)不丟失,需要將數(shù)據(jù)從內(nèi)存中以某種形式同步到硬盤中,這一過程就是持久化,本文重點(diǎn)給大家介紹Redis RDB技術(shù)底層原理實(shí)現(xiàn)方法,一起看看吧
    2021-09-09
  • redis 集群批量操作實(shí)現(xiàn)

    redis 集群批量操作實(shí)現(xiàn)

    這篇文章主要介紹了redis 集群批量操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評論

樟树市| 平度市| 嵊泗县| 调兵山市| 顺平县| 巴楚县| 定远县| 南投市| 松阳县| 确山县| 寿宁县| 始兴县| 鹿邑县| 图们市| 渝北区| 江口县| 盈江县| 夏河县| 门头沟区| 洛隆县| 灵台县| 洛浦县| 吐鲁番市| 东乡| 台中县| 天峻县| 聂荣县| 平果县| 寿光市| 肇州县| 突泉县| 二手房| 综艺| 闸北区| 时尚| 上杭县| 遵义县| 客服| 洞头县| 大足县| 晋江市|