码迷,mamicode.com
首页 > 其他好文 > 详细

高并发学习之使用RateLimiter实现令牌桶限流

时间:2020-02-12 14:52:44      阅读:193      评论:0      收藏:0      [点我收藏+]

标签:nes   16px   The   说明   depend   切面   cer   table   create   

    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

高并发学习之使用RateLimiter实现令牌桶限流

标签:nes   16px   The   说明   depend   切面   cer   table   create   

原文地址:https://www.cnblogs.com/sunfie/p/12298892.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!