• 高并发学习之使用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

  • 相关阅读:
    初识react hooks
    react初识生命周期
    在调用setState之后发生了什么
    课后作业四
    课后作业2
    课后作业1
    自我介绍
    电脑软件推荐
    数据结构
    数组(一维数组)
  • 原文地址:https://www.cnblogs.com/sunfie/p/12298892.html
Copyright © 2020-2023  润新知