스프링 MVC 간단한 전류 제한 디자인

첫째, 개념

현재 제한 목적이 동시 액세스에 의해 보호됩니다 / 속도 요청, 또는 시간 윈도우 시스템 속도 내에서 요청을 제한, 속도 제한은 서비스가 거부 될 수 있습니다 한 번에 도달하거나, 큐, 강등 프로세스를 기다리고 있습니다.

: 일반적으로 두 가지 방법으로 사용되는 알고리즘 제한 리키 버킷토큰 버킷 알고리즘을 :

리키 버킷 알고리즘은 물의 속도 어셈블리가 토큰 버킷 알고리즘은 데이터 전송 속도에 대한 제한을 부과 할 수 있음을 알 수있다 플로우를 통해 직접적으로 흐르는 제 버킷에 드레인 매우 간단한 아이디어 물 (요청), 일정 속도로 물 누설 버킷이다.

많은 시나리오의 경우, 평균 데이터 전송 속도에 부가 된 청구항을 제한 할 수 있고, 또한 허용 버스트 전송 어느 정도 필요하다. 이번에 부적절한 리키 버킷 알고리즘 일 수 있고, 토큰 버킷 알고리즘은 더욱 적합하다.

토큰 버킷 알고리즘 원리는 시스템이 버킷에서의 토큰으로 일정한 속도 것이라고하지만, 요청이 처리 될 경우, 토큰 버킷이 후, 바람직하지 않은 토큰 버킷을 얻기 위해 필요로 시작 서비스 거부.

둘째, 응용 프로그램

구글 오픈 소스 툴킷 구아바는 전류 제한 도구 RateLimiter, 매우 사용하기 쉽고, 전류 제한 완료 클래스 토큰 버킷 알고리즘을 제공합니다. RateLimiter API는 동시 프로그래밍 네트워크 볼 수 있습니다 구아바 RateLimiter는 소개했다.

우리는 구아바 RateLimiter 우리 제한 계획을 달성 MVC 인터셉터를 사용 + :

@Slf4j
public class RequestLimitInterceptor extends HandlerInterceptorAdapter implements BeanPostProcessor {

    private static final Integer GLOBAL_RATE_LIMITER = 10;

    private static Map<PatternsRequestCondition, RateLimiter> URL_RATE_MAP;

    private Properties urlProperties;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (URL_RATE_MAP != null) {
            String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
            for (PatternsRequestCondition patternsRequestCondition : URL_RATE_MAP.keySet()) {
                //使用spring DispatcherServlet的匹配器PatternsRequestCondition进行匹配
                //spring 3.x 版本
                //Set<String> matches = patternsRequestCondition.getMatchingCondition(request).getPatterns();
                //spring 4.x 版本
                List<String> matches = patternsRequestCondition.getMatchingPatterns(lookupPath);
                if (CollectionUtils.isEmpty(matches)){
                    continue;
                }
                //尝试获取令牌
                if (!URL_RATE_MAP.get(patternsRequestCondition).tryAcquire(1000, TimeUnit.MILLISECONDS)) {
                    log.info(" 请求'{}'匹配到 mathes {},超过限流速率,获取令牌失败。", lookupPath, Joiner.on(",").join(patternsRequestCondition.getPatterns()));
                    return false;
                }
                log.info(" 请求'{}'匹配到 mathes {} ,成功获取令牌,进入请求。", lookupPath, Joiner.on(",").join(patternsRequestCondition.getPatterns()));
            }
        }
        return super.preHandle(request, response, handler);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (RequestMappingHandlerMapping.class.isAssignableFrom(bean.getClass())) {
            if (URL_RATE_MAP == null) {
                URL_RATE_MAP = new ConcurrentHashMap<>(16);
            }
            log.info("we get all the controllers's methods and assign it to urlRateMap");
            RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) bean;
            Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
            for (RequestMappingInfo mappingInfo : handlerMethods.keySet()) {
                PatternsRequestCondition requestCondition = mappingInfo.getPatternsCondition();
                // 默认的 url 限流方案设定
                URL_RATE_MAP.put(requestCondition, RateLimiter.create(GLOBAL_RATE_LIMITER));
            }
            // 自定义的限流方案设定
            if (urlProperties != null) {
                for (String urlPatterns : urlProperties.stringPropertyNames()) {
                    String limit = urlProperties.getProperty(urlPatterns);
                    if (!limit.matches("^-?\\d+$")){
                        log.error("the value {} for url patterns {} is not a number ,please check it ", limit, urlPatterns);
                    }
                    URL_RATE_MAP.put(new PatternsRequestCondition(urlPatterns), RateLimiter.create(Integer.parseInt(limit)));
                }
            }
        }
        return bean;
    }

    /**
     * 限流的 URL与限流值的 K/V 值
     *
     * @param urlProperties
     */
    public void setUrlProperties(Properties urlProperties) {
        this.urlProperties = urlProperties;
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public RequestLimitInterceptor requestLimitInterceptor(){
        RequestLimitInterceptor limitInterceptor = new RequestLimitInterceptor();
        // 设置自定义的 url 限流方案
        Properties properties = new Properties();
        properties.setProperty("/admin/**", "10");
        limitInterceptor.setUrlProperties(properties);
        return limitInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 限流方案
        registry.addInterceptor(requestLimitInterceptor());
    }
}

팁 : 여기에 합리적인되지 목록 urlProperties을 제한하는 사용자 지정 프로그램이 구성 센터 (Nacos, 봄 클라우드 구성 등)에서 고려 될 수있다 동적 필요를 제한 URL을 업데이트합니다.

참고 히로부미 :

  1. https://blog.csdn.net/Lili429/article/details/79236819
  2. https://blog.csdn.net/valleychen1111/article/details/78038366

추천

출처www.cnblogs.com/jmcui/p/11281788.html