WANG 5 jaren geleden
bovenliggende
commit
29f2358905

+ 1 - 1
src/main/java/cn/com/qmth/examcloud/web/actuator/ApiStatusEndpoint.java

@@ -58,7 +58,7 @@ public class ApiStatusEndpoint {
 		List<ReportInfo> reportInfoList = ReportorHolder.getApiDataReportor().getReportInfoList();
 
 		ReportInfo reportInfo = reportInfoList.get(0);
-		List<ApiStatusInfo> list = apiStatusInfoCollector.refresh(reportInfo);
+		List<ApiStatusInfo> list = apiStatusInfoCollector.collect(reportInfo);
 
 		Collections.sort(list, new Comparator<ApiStatusInfo>() {
 			@Override

+ 1 - 1
src/main/java/cn/com/qmth/examcloud/web/actuator/ApiStatusInfoCollector.java

@@ -21,7 +21,7 @@ public class ApiStatusInfoCollector {
 			.forRegistry(MetricRegistryHolder.getDefalut()).convertRatesTo(TimeUnit.SECONDS)
 			.convertDurationsTo(TimeUnit.MILLISECONDS).build();
 
-	public synchronized List<ApiStatusInfo> refresh(ReportInfo reportInfo) {
+	public synchronized List<ApiStatusInfo> collect(ReportInfo reportInfo) {
 
 		ReportorHolder.getApiDataReportor().report();
 		consoleReporter.report();

+ 131 - 0
src/main/java/cn/com/qmth/examcloud/web/interceptor/ApiFlowLimitedInterceptor.java

@@ -1,10 +1,35 @@
 package cn.com.qmth.examcloud.web.interceptor;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
 import org.springframework.web.servlet.HandlerInterceptor;
 
+import com.google.common.collect.Maps;
+import com.googlecode.aviator.AviatorEvaluator;
+
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
+import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
+import cn.com.qmth.examcloud.commons.util.PathUtil;
+import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
+import cn.com.qmth.examcloud.commons.util.Util;
+import cn.com.qmth.examcloud.web.actuator.ApiStatusInfo;
+import cn.com.qmth.examcloud.web.actuator.ApiStatusInfoCollector;
+import cn.com.qmth.examcloud.web.actuator.ReportInfo;
+import cn.com.qmth.examcloud.web.actuator.ReportorHolder;
+import cn.com.qmth.examcloud.web.enums.HttpServletRequestAttribute;
+import cn.com.qmth.examcloud.web.support.ApiInfo;
+import cn.com.qmth.examcloud.web.support.ServletUtil;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+import cn.com.qmth.examcloud.web.support.StatusResponse;
+
 /**
  * API 流量限制截器
  *
@@ -14,10 +39,116 @@ import org.springframework.web.servlet.HandlerInterceptor;
  */
 public class ApiFlowLimitedInterceptor implements HandlerInterceptor {
 
+	private static final ExamCloudLog LOG = ExamCloudLogFactory
+			.getLog(ApiFlowLimitedInterceptor.class);
+
+	private static Map<String, ApiStatusInfo> apiInfoMap = Maps.newHashMap();
+
+	private static Properties props = new Properties();
+
+	static {
+
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+				Util.sleep(10);
+				while (true) {
+					try {
+						ReportorHolder.getApiDataReportor().report();
+						List<ReportInfo> reportInfoList = ReportorHolder.getApiDataReportor()
+								.getReportInfoList();
+
+						ReportInfo reportInfo = reportInfoList.get(0);
+						ApiStatusInfoCollector apiStatusInfoCollector = SpringContextHolder
+								.getBean(ApiStatusInfoCollector.class);
+						List<ApiStatusInfo> list = apiStatusInfoCollector.collect(reportInfo);
+
+						Map<String, ApiStatusInfo> newApiInfoMap = list.stream()
+								.collect(Collectors.toMap(ApiStatusInfo::getMapping, info -> info));
+
+						apiInfoMap = newApiInfoMap;
+
+					} catch (Exception e) {
+						LOG.error("fail to refresh API status.", e);
+					}
+				}
+			}
+		}).start();
+
+		new Thread(new Runnable() {
+			@Override
+			public void run() {
+
+				while (true) {
+					try {
+						Properties newProps = new Properties();
+
+						PropertiesUtil.loadFromResource(
+								PathUtil.getResoucePath("limited.properties"), newProps);
+
+						props = newProps;
+
+					} catch (Exception e) {
+						LOG.error("fail to refresh API config.", e);
+					}
+					Util.sleep(10);
+				}
+			}
+		}).start();
+
+	}
+
 	@Override
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
 			Object handler) throws Exception {
 
+		ApiInfo apiInfo = (ApiInfo) request
+				.getAttribute(HttpServletRequestAttribute.$_API_INFO.name());
+
+		if (null == apiInfo) {
+			return true;
+		}
+
+		ApiStatusInfo apiStatusInfo = null;
+		try {
+			apiStatusInfo = apiInfoMap.get(apiInfo.getMapping());
+		} catch (Exception e) {
+			// ignore
+		}
+
+		if (null == apiStatusInfo) {
+			return true;
+		}
+
+		String expression = null;
+		try {
+			expression = (String) props.get(apiInfo.getMapping());
+		} catch (Exception e) {
+			// ignore
+		}
+		if (StringUtils.isBlank(expression)) {
+			return true;
+		}
+		expression = expression.trim();
+
+		Map<String, Object> env = Maps.newHashMap();
+		env.put("mean", apiStatusInfo.getMean());
+		env.put("max", apiStatusInfo.getMax());
+		env.put("min", apiStatusInfo.getMin());
+		env.put("meanRate", apiStatusInfo.getMeanRate());
+		env.put("errPercent", apiStatusInfo.getErrorPercent());
+		env.put("errMeanRate", apiStatusInfo.getErrorMeanRate());
+		boolean limited = false;
+		try {
+			limited = (Boolean) AviatorEvaluator.execute(expression, env);
+		} catch (Exception e) {
+			LOG.error("fail to get value of expression. expression= " + expression, e);
+		}
+		if (limited) {
+			response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
+			ServletUtil.returnJson(new StatusResponse("503", "系统超负荷,请稍后重试"), response);
+			return false;
+		}
 		return true;
 	}