高並發學習之使用RateLimiter實現令牌桶限流


    RateLimiter是guava提供的基於令牌桶算法的實現類,可以非常簡單的完成限流特技,並且根據系統的實際情況來調整生成token的速率。
通常可應用於搶購限流防止沖垮系統;限制某接口、服務單位時間內的訪問量,譬如一些第三方服務會對用戶訪問量進行限制;限制網速,單位時間內只允許上傳下載多少字節等。

guava的maven依賴

<dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>25.1-jre</version>
 </dependency>

 

 

 

    令牌桶的原理,有一個獨立線程一直以一個固定的速率往桶中存放令牌,客戶端去桶中獲取令牌,獲取到令牌,就可以訪問,獲取不到,說明請求過多,需要服務降級。

示例代碼:

(1)請求限流注解

/**
 * @創建人: hadoop
 * @創建時間: 2020/2/12
 * @描述:
 */
@Target(value = {ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface LimitingAnnotation {

    /**
     * 獲取令牌超時時間
     */
    long timeOut() default 0L;

    /**
     * 限流速率,每秒最多產生令牌數
     */
    long produceRate() default 1000L;
    
}

(2)請求限流切面

/**
 * @創建人: hadoop
 * @創建時間: 2020/2/12
 * @描述: 服務限流降級切面,防止每秒請求數過多
 */
@Component
@Aspect
@Slf4j
public class LimitingAop {

    @Value("${request.limit}")
    private Integer limitValue ;

    @Autowired
    private HttpServletResponse response;

    // 令牌桶
    // limitValue : 表示每秒中生成limitValue個令牌存放在桶中
    @SuppressWarnings("UnstableApiUsage")
    private RateLimiter rateLimiter = RateLimiter.create(limitValue);

    /**
     * 限流切點
     */
    @Pointcut("@annotation(com.service.bussiness.aop.LimitingAnnotation)")
    public void limitingPointCut() {
    }

    @SuppressWarnings({"rawtypes", "UnstableApiUsage"})
    @Around("limitingPointCut()")
    public Object controllerAround( ProceedingJoinPoint point ) {
        String description = CommonConst.EMPTYSTRING;
        Method method = null;
        String methodName = point.getSignature().getName();
        Class[] paramTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
        try {
            method = point.getTarget().getClass().getMethod(methodName, paramTypes);
            if ( !method.isAnnotationPresent(LimitingAnnotation.class) ) {
                return null;
            }
        } catch ( NoSuchMethodException e ) {
            log.error("限流切面出現異常,異常原因是: " + e.getMessage());
            throw new CustomException(
                    Integer.parseInt(CustomExceptionType.SYSTEM_ERROR.getCode()) ,
                    e.getMessage(),
                    "限流切面出現異常",
                    "請求的目標方法不存在"
            );
        }
        LimitingAnnotation limitingAnnotation =
                method.getAnnotation(LimitingAnnotation.class);
        if ( null == limitingAnnotation ){
            return null;
        }
        long timeOut = limitingAnnotation.timeOut();
        long produceRate = limitingAnnotation.produceRate();
        // 設置限流速率,每秒產生多少令牌
        rateLimiter.setRate(produceRate);
        // 每次發送請求,在設定ms內沒有獲取到令牌,則對服務進行降級處理
        boolean acquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
        if ( !acquire ){
            getErrorMsg();
            return null;
        }
        try {
            return point.proceed();
        } catch (Throwable throwable) {
            log.error(methodName+"請求出現異常,異常原因是: " + throwable.getMessage());
            throwable.printStackTrace();
        }
        return null;
    }

    /**
     * 向客戶端輸出服務降級信息
     *
     */
    public void getErrorMsg(){
       response.setHeader("Content-Type","application/json;charset=UTF-8");
        PrintWriter printWriter = null;
        try {
            printWriter = response.getWriter();
            Map<String,Object> resultMap = new HashMap<>();
            resultMap.put("msg","前方任務過多,請稍后再試");
            printWriter.write(JSON.toJSONString(resultMap));
            printWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

(3) 請求服務

    @RequestMapping(value = "/search", method = RequestMethod.POST, consumes = "application/json")
    @ResponseBody
    @LimitingAnnotation( timeOut = 500, produceRate = 1000 )
    public ResponseEntity<String> search( @RequestBody String param ) 
{
return this.searchService.searchBillInfo( xEncryption , params ); }

參考:

https://www.cnblogs.com/pickKnow/p/11252519.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM