Java tutorial
/******************************************************************************* * Copyright (C) 2018 hankai * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ******************************************************************************/ package ren.hankai.cordwood.web.security.support; import com.google.common.util.concurrent.RateLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExecutionChain; import ren.hankai.cordwood.web.security.AccessLimiter; import ren.hankai.cordwood.web.security.annotation.Stabilized; import java.util.HashMap; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletResponse; /** * ? google guava ?? * * @author hankai * @version 1.0.0 * @since Oct 12, 2018 10:59:23 AM */ @Component public class DefaultAccessLimiter implements AccessLimiter { private static final Logger logger = LoggerFactory.getLogger(DefaultAccessLimiter.class); /** * ??????? */ private final HashMap<HandlerMethod, RateLimitInfo> rateLimitMappings = new HashMap<>(64); /** * ??????? * * @param stabilized ? * @return ??? * @author hankai * @since Oct 12, 2018 11:46:00 AM */ private String getStabilizationDescription(Stabilized stabilized) { return String.format("{ maxQps=%.2f, timeout=%d, warmupPeriod=%d, fusingInterval=%d, fusingThreshold=%d }", stabilized.maxQps(), stabilized.timeout(), stabilized.warmupPeriod(), stabilized.fusingInterval(), stabilized.fusingThreshold()); } /** * ??? * * @param handlerMethod ? * @return * @author hankai * @since Oct 12, 2018 11:03:07 AM */ private Stabilized getStabilizations(HandlerMethod handlerMethod) { // Stabilized anno = handlerMethod.getMethodAnnotation(Stabilized.class); if (null == anno) { // ? final Class<?> beanType = handlerMethod.getBeanType(); anno = beanType.getAnnotation(Stabilized.class); } if (anno != null) { logger.debug( String.format("Access limit configuration: \n\n%s\n\n", getStabilizationDescription(anno))); } return anno; } @Override public boolean handleAccess(HandlerExecutionChain requestHandler, HttpServletResponse response) { final Object obj = requestHandler.getHandler(); if (null != obj) { if (obj instanceof HandlerMethod) { final HandlerMethod hm = (HandlerMethod) obj; final Stabilized anno = getStabilizations(hm); // ?? if (null != anno) { // ???? RateLimitInfo limitInfo = rateLimitMappings.get(hm); if (null == limitInfo) { // ??? final RateLimiter limiter = RateLimiter.create(anno.maxQps(), anno.warmupPeriod(), TimeUnit.SECONDS); limitInfo = new RateLimitInfo(limiter); rateLimitMappings.put(hm, limitInfo); } else { logger.debug(String.format( "Access hit limit mapping, selected handler mapping was:\n\n %s\n", hm.toString())); } if (limitInfo.isFused(anno)) { // logger.debug("Fused, service will be unavailable temporarily."); response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value()); return false; } else { // ??? final boolean success = limitInfo.getLimiter().tryAcquire(1, anno.timeout(), TimeUnit.MICROSECONDS); if (success) { limitInfo.resetFailures(); // ???? logger.debug("Token acquired, service is now available."); } else { logger.debug("Failed to acquire token, service will be unavailable temporarily."); limitInfo.addFailures(); // ? response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value()); return false; } } } } } return true; } }