首頁 > Java > java教程 > 主體

接口限流,騷操作全在這裡了!

發布: 2023-07-26 14:22:33
轉載
1124 人瀏覽過

為什麼限流

系統在設計的時候,我們會有一個系統的預估容量,長時間超過系統能承受的TPS/QPS閾值,系統有可能會被壓垮,最終導致整個服務不可用。為了避免這種情況,我們需要對介面請求進行限流。

所以,我們可以透過對並發存取請求進行限速或一個時間窗口內的請求數量進行限速來保護系統或避免不必要的資源浪費,一旦達到限制速率則可以拒絕服務、排隊或等待。

限流背景

系統有一個獲取手機簡訊驗證碼的接口,因為是開放接口,所以為了避免用戶不斷的發送請求取得驗證碼,防止惡意刷介面的情況發生,於是用最簡單的計數器方式做了限流,限制每個IP每分鐘只能請求一次,然後其他每個手機號碼的時間窗口限制則是透過業務邏輯進行判斷。一般一些介面訪問量比較大的,可能會壓垮系統的,則需要加入流量限制!如:秒殺等...

實作方式

#1.引入依賴

#
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
登入後複製

2.自訂限流註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
    /**
     * 限流key
     */
     String key() default Constants.RATE_LIMIT_KEY;

    /**
     * 限流时间,单位秒
     */
     int time() default 60;

    /**
     * 限流次数
     */
    int count() default 100;

    /**
     * 限流类型
     */
    LimitType limitType() default LimitType.DEFAULT;

    /**
     * 限流后返回的文字
     */
    String limitMsg() default "访问过于频繁,请稍候再试";
}
登入後複製

3.限流切面

@Aspect
@Component
public class RateLimiterAspect {

    private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);

    @Autowired
    private RedisUtils redisUtils;

    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        long total = 1L;

        String combineKey = getCombineKey(rateLimiter, point);
        try
        {
            if(redisUtils.hasKey(combineKey)){
                total = redisUtils.incr(combineKey,1);  //请求进来,对应的key加1
                if(total > count)
                    throw new ServiceRuntimeException(rateLimiter.limitMsg());
            }else{
                redisUtils.set(combineKey,1,time);  //初始化key
            }
        }
        catch (ServiceRuntimeException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new ServiceRuntimeException("网络繁忙,请稍候再试");
        }
    }

    /**
     * 获取限流key
     * @param rateLimiter
     * @param point
     * @return
     */
    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
        StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
        if (rateLimiter.limitType() == LimitType.IP)
        {
            stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
        return stringBuffer.toString();
    }

}
登入後複製

4.寫一個簡單的介面進行測試

@RestController
public class TestController {

    @RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分钟内只能请求一次,请稍后重试")
    @GetMapping("/hello")
    public ResultMsg hello() {
        return ResultMsg.success("Hello World!");
    }
}
登入後複製

#5.全域例外攔截

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceRuntimeException.class)
    public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request)
    {
        return ResultMsg.error(e.getMessage());
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public ResultMsg handleException(Exception e, HttpServletRequest request)
    {
        return ResultMsg.error("系统异常");
    }

}
登入後複製

6.介面測試

1)第一次發送,正常返回結果

接口限流,騷操作全在這裡了!

2)一分鐘內第二次發送,返回錯誤,限流提示

接口限流,騷操作全在這裡了!

以上,就是AOP Redis 的方案實作介面限流,你學廢了嗎?

還有其他的限流方式,如滑動視窗限流方式(比計數器更嚴謹)、令牌桶等...,有興趣的小夥伴可以學習一下。


#

以上是接口限流,騷操作全在這裡了!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:Java学习指南
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!