WANG 5 anni fa
parent
commit
c3a6721710

+ 111 - 14
src/main/java/cn/com/qmth/examcloud/web/interceptor/ApiFlowLimitedInterceptor.java

@@ -1,6 +1,7 @@
 package cn.com.qmth.examcloud.web.interceptor;
 
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Properties;
 
 import javax.servlet.http.HttpServletRequest;
@@ -52,6 +53,10 @@ public class ApiFlowLimitedInterceptor implements HandlerInterceptor {
 
 	private static Properties props = new Properties();
 
+	private static Map<String, RateLimiter> limiterMap = Maps.newConcurrentMap();
+
+	private static Map<String, Long> permitsPerSecondMap = Maps.newConcurrentMap();
+
 	static {
 
 		double permitsPerSecond = PropertyHolder.getInt("examcloud.api.permitsPerSecond", 100000);
@@ -61,28 +66,102 @@ public class ApiFlowLimitedInterceptor implements HandlerInterceptor {
 		allowedRate = PropertyHolder.getInt("examcloud.api.flowLimited.allowedRate", 5);
 		minCallRate = PropertyHolder.getInt("examcloud.api.flowLimited.minCallRate", 10);
 
+		refreshConfig();
+
+		for (Entry<Object, Object> entry : props.entrySet()) {
+			String key = (String) entry.getKey();
+			String value = (String) entry.getValue();
+
+			if (key.endsWith("[S]")) {
+				Long curPermitsPerSecond = null;
+				try {
+					curPermitsPerSecond = Long.parseLong(value);
+				} catch (NumberFormatException e) {
+					LOG.error("error value. key= " + key, e);
+					continue;
+				}
+
+				if (curPermitsPerSecond < 0) {
+					LOG.error("value is less than 0. key= " + key);
+				}
+
+				if (curPermitsPerSecond > 10000) {
+					LOG.error("value is more than 10000. key= " + key);
+				}
+
+				RateLimiter curRateLimiter = RateLimiter.create(curPermitsPerSecond);
+				limiterMap.put(key, curRateLimiter);
+				permitsPerSecondMap.put(key, curPermitsPerSecond);
+			}
+		}
+
 		new Thread(new Runnable() {
 			@Override
 			public void run() {
 
 				while (true) {
-					try {
-						Properties newProps = new Properties();
+					refreshConfig();
+
+					for (Entry<String, Long> entry : permitsPerSecondMap.entrySet()) {
+						String key = entry.getKey();
+						Long value = entry.getValue();
+
+						String curValue = (String) props.get(key);
+						Long curPermitsPerSecond = null;
+						try {
+							curPermitsPerSecond = Long.parseLong(curValue);
+						} catch (NumberFormatException e) {
+							LOG.error("error value. key= " + key, e);
+							continue;
+						}
+
+						if (curPermitsPerSecond < 0) {
+							LOG.error("value is less than 0. key= " + key);
+						}
+
+						if (curPermitsPerSecond > 10000) {
+							LOG.error("value is more than 10000. key= " + key);
+						}
+
+						if (!value.equals(curPermitsPerSecond)) {
+							RateLimiter curRateLimiter = limiterMap.get(key);
+							curRateLimiter.setRate(curPermitsPerSecond);
+						}
 
-						PropertiesUtil.loadFromResource("limited.properties", newProps);
-
-						props = newProps;
-
-					} catch (Exception e) {
-						LOG.error("fail to refresh API config.", e);
 					}
-					Util.sleep(10);
+
+					Util.sleep(30);
 				}
 			}
+
 		}).start();
 
 	}
 
+	private static void refreshConfig() {
+		try {
+			Properties newProps = new Properties();
+
+			Properties nextProps = new Properties();
+
+			PropertiesUtil.loadFromResource("limited.properties", newProps);
+			for (Entry<Object, Object> entry : newProps.entrySet()) {
+				String key = (String) entry.getKey();
+				String value = (String) entry.getValue();
+				if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
+					continue;
+				}
+
+				nextProps.put(key.trim(), value.trim());
+			}
+
+			props = nextProps;
+
+		} catch (Exception e) {
+			LOG.error("fail to refresh API config.", e);
+		}
+	}
+
 	@Override
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
 			Object handler) throws Exception {
@@ -121,11 +200,29 @@ public class ApiFlowLimitedInterceptor implements HandlerInterceptor {
 			return true;
 		}
 
+		String rateLimiterKey = apiInfo.getMapping() + "[S]";
+		RateLimiter limiter = limiterMap.get(rateLimiterKey);
+
+		if (null != limiter) {
+			acquired = limiter.tryAcquire();
+			if (!acquired) {
+
+				if (INTERFACE_LOG.isDebugEnabled()) {
+					INTERFACE_LOG.debug("[Limited]. ");
+				}
+
+				response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
+				ServletUtil.returnJson(new StatusResponse("503", "limited"), response);
+				return false;
+			}
+		}
+
 		String expression = null;
+		String expressionKey = apiInfo.getMapping() + "[E]";
 		try {
-			expression = (String) props.get(apiInfo.getMapping());
+			expression = (String) props.get(expressionKey);
 		} catch (Exception e) {
-			LOG.error("error value. key= " + apiInfo.getMapping(), e);
+			LOG.error("error value. key= " + expressionKey, e);
 		}
 		if (StringUtils.isBlank(expression)) {
 			return true;
@@ -157,14 +254,14 @@ public class ApiFlowLimitedInterceptor implements HandlerInterceptor {
 				double oneMinuteRate = apiStatusInfo.getOneMinuteRate();
 
 				int curMinCallRate = minCallRate;
-				String key = apiInfo.getMapping() + "[R]";
+				String minCallRateKey = apiInfo.getMapping() + "[R]";
 				try {
-					String value = (String) props.get(key);
+					String value = (String) props.get(minCallRateKey);
 					if (StringUtils.isNotBlank(value)) {
 						curMinCallRate = Integer.parseInt(value.trim());
 					}
 				} catch (Exception e) {
-					LOG.error("error value. key= " + key, e);
+					LOG.error("error value. key= " + minCallRateKey, e);
 				}
 
 				if (LOG.isDebugEnabled()) {