WANG 5 năm trước cách đây
mục cha
commit
3378523007

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

@@ -5,6 +5,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
 import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
@@ -12,6 +13,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.MediaType;
 
+import com.codahale.metrics.ConsoleReporter;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -43,6 +45,10 @@ public class ApiStatusEndpoint {
 		return get("");
 	}
 
+	private static ConsoleReporter consoleReporter = ConsoleReporter
+			.forRegistry(MetricRegistryHolder.getDefalut()).convertRatesTo(TimeUnit.SECONDS)
+			.convertDurationsTo(TimeUnit.MILLISECONDS).build();
+
 	/**
 	 * url: /actuator/api-status/ignored?order=XX
 	 *
@@ -55,69 +61,78 @@ public class ApiStatusEndpoint {
 
 		System.out.println("order by " + order);
 		ReportorHolder.getApiDataReportor().report();
+		consoleReporter.report();
 		List<ReportInfo> reportInfoList = ReportorHolder.getApiDataReportor().getReportInfoList();
 
 		ReportInfo reportInfo = reportInfoList.get(0);
+		List<HistogramInfo> histogramInfoList = reportInfo.getHistogramInfoList();
 		List<MeterInfo> meterInfoList = reportInfo.getMeterInfoList();
-		List<TimerInfo> timerInfoList = reportInfo.getTimerInfoList();
-
-		Map<String, MeterInfo> meterInfoMap = Maps.newHashMap();
 
+		Map<String, MeterInfo> apiErrMeterInfoMap = Maps.newHashMap();
 		for (MeterInfo meterInfo : meterInfoList) {
 			String key = meterInfo.getKey();
-			if (!key.startsWith(MetricNames.API_ERROR_METER.name())) {
-				continue;
+			if (key.startsWith(MetricNames.API_ERROR_METER.name())) {
+				String mapping = key.substring(MetricNames.API_ERROR_METER.name().length() + 1);
+				apiErrMeterInfoMap.put(mapping, meterInfo);
+			}
+		}
+
+		Map<String, HistogramInfo> apiHistogramInfoMap = Maps.newHashMap();
+		for (HistogramInfo histogramInfo : histogramInfoList) {
+			String key = histogramInfo.getKey();
+			if (key.startsWith(MetricNames.API_HISTOGRAM.name())) {
+				String mapping = key.substring(MetricNames.API_HISTOGRAM.name().length() + 1);
+				apiHistogramInfoMap.put(mapping, histogramInfo);
 			}
-			String mapping = key.substring(MetricNames.API_ERROR_METER.name().length() + 1);
-			meterInfoMap.put(mapping, meterInfo);
 		}
 
 		List<ApiStatusInfo> list = Lists.newArrayList();
 
-		for (TimerInfo timerInfo : timerInfoList) {
-			String key = timerInfo.getKey();
-			if (!key.startsWith(MetricNames.API_TIMER.name())) {
-				continue;
-			}
+		for (MeterInfo cur : meterInfoList) {
+			String key = cur.getKey();
+			if (key.startsWith(MetricNames.API_METER.name())) {
 
-			String mapping = key.substring(MetricNames.API_TIMER.name().length() + 1);
+				String mapping = key.substring(MetricNames.API_METER.name().length() + 1);
 
-			ApiStatusInfo apiStatusInfo = new ApiStatusInfo();
-			list.add(apiStatusInfo);
+				ApiStatusInfo apiStatusInfo = new ApiStatusInfo();
+				list.add(apiStatusInfo);
 
-			apiStatusInfo.setMapping(mapping);
+				apiStatusInfo.setMapping(mapping);
 
-			ApiInfo apiInfo = ApiInfoHolder.getApiInfo(mapping);
-			if (null != apiInfo) {
-				apiStatusInfo.setDescription(apiInfo.getDescription());
-			}
+				ApiInfo apiInfo = ApiInfoHolder.getApiInfo(mapping);
+				if (null != apiInfo) {
+					apiStatusInfo.setDescription(apiInfo.getDescription());
+				}
 
-			apiStatusInfo.setCount(timerInfo.getCount());
-			apiStatusInfo.setMeanRate(timerInfo.getMeanRate());
-			apiStatusInfo.setOneMinuteRate(timerInfo.getOneMinuteRate());
-			apiStatusInfo.setFiveMinuteRate(timerInfo.getFiveMinuteRate());
-			apiStatusInfo.setFifteenMinuteRate(timerInfo.getFifteenMinuteRate());
-
-			apiStatusInfo.setMin(timerInfo.getMin());
-			apiStatusInfo.setMax(timerInfo.getMax());
-			apiStatusInfo.setMean(timerInfo.getMean());
-
-			apiStatusInfo.setP50(timerInfo.getP50());
-			apiStatusInfo.setP75(timerInfo.getP75());
-			apiStatusInfo.setP95(timerInfo.getP95());
-			apiStatusInfo.setP98(timerInfo.getP98());
-			apiStatusInfo.setP99(timerInfo.getP99());
-
-			MeterInfo meterInfo = meterInfoMap.get(mapping);
-			if (null == meterInfo) {
-				apiStatusInfo.setErrorCount(0L);
-				apiStatusInfo.setErrorPercent(0D);
-			} else {
-				apiStatusInfo.setErrorCount(meterInfo.getCount());
-				apiStatusInfo.setErrorPercent(
-						getPercent(meterInfo.getMeanRate(), timerInfo.getMeanRate()));
-			}
+				apiStatusInfo.setCount(cur.getCount());
+				apiStatusInfo.setMeanRate(cur.getMeanRate());
+				apiStatusInfo.setOneMinuteRate(cur.getOneMinuteRate());
+				apiStatusInfo.setFiveMinuteRate(cur.getFiveMinuteRate());
+				apiStatusInfo.setFifteenMinuteRate(cur.getFifteenMinuteRate());
+
+				HistogramInfo histogramInfo = apiHistogramInfoMap.get(mapping);
+
+				apiStatusInfo.setMin(histogramInfo.getMin());
+				apiStatusInfo.setMax(histogramInfo.getMax());
+				apiStatusInfo.setMean(histogramInfo.getMean());
+
+				apiStatusInfo.setP50(histogramInfo.getP50());
+				apiStatusInfo.setP75(histogramInfo.getP75());
+				apiStatusInfo.setP95(histogramInfo.getP95());
+				apiStatusInfo.setP98(histogramInfo.getP98());
+				apiStatusInfo.setP99(histogramInfo.getP99());
+
+				MeterInfo apiErrMeterInfo = apiErrMeterInfoMap.get(mapping);
+				if (null == apiErrMeterInfo) {
+					apiStatusInfo.setErrorCount(0L);
+					apiStatusInfo.setErrorPercent(0D);
+				} else {
+					apiStatusInfo.setErrorCount(apiErrMeterInfo.getCount());
+					apiStatusInfo.setErrorPercent(
+							getPercent(apiErrMeterInfo.getMeanRate(), cur.getMeanRate()));
+				}
 
+			}
 		}
 
 		Collections.sort(list, new Comparator<ApiStatusInfo>() {

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

@@ -126,11 +126,21 @@ public class DataReportor extends ScheduledReporter {
 
 		ReportInfo info = new ReportInfo();
 		info.setDateTime(new Date(clock.getTime()));
-		final List<MeterInfo> meterInfoList = Lists.newArrayList();
+
+		final List<HistogramInfo> histogramInfoList = Lists.newArrayList();
 		final List<TimerInfo> timerInfoList = Lists.newArrayList();
+		final List<MeterInfo> meterInfoList = Lists.newArrayList();
+		info.setHistogramInfoList(histogramInfoList);
 		info.setMeterInfoList(meterInfoList);
 		info.setTimerInfoList(timerInfoList);
 
+		if (!histograms.isEmpty()) {
+			for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
+				HistogramInfo histogramInfo = getHistogramInfo(entry.getKey(), entry.getValue());
+				histogramInfoList.add(histogramInfo);
+			}
+		}
+
 		if (!meters.isEmpty()) {
 			for (Map.Entry<String, Meter> entry : meters.entrySet()) {
 				MeterInfo meterInfo = getMeterInfo(entry.getKey(), entry.getValue());
@@ -178,6 +188,43 @@ public class DataReportor extends ScheduledReporter {
 		return info;
 	}
 
+	private HistogramInfo getHistogramInfo(String key, Histogram histogram) {
+		Snapshot snapshot = histogram.getSnapshot();
+		HistogramInfo info = new HistogramInfo();
+		info.setKey(key);
+
+		if (isEnabled(MetricAttribute.MIN)) {
+			info.setMin(snapshot.getMin());
+		}
+		if (isEnabled(MetricAttribute.MAX)) {
+			info.setMax(snapshot.getMax());
+		}
+		if (isEnabled(MetricAttribute.MEAN)) {
+			info.setMean(snapshot.getMean());
+		}
+
+		if (isEnabled(MetricAttribute.P50)) {
+			info.setP50(snapshot.getMedian());
+		}
+		if (isEnabled(MetricAttribute.P75)) {
+			info.setP75(snapshot.get75thPercentile());
+		}
+		if (isEnabled(MetricAttribute.P95)) {
+			info.setP95(snapshot.get95thPercentile());
+		}
+		if (isEnabled(MetricAttribute.P98)) {
+			info.setP98(snapshot.get98thPercentile());
+		}
+		if (isEnabled(MetricAttribute.P99)) {
+			info.setP99(snapshot.get99thPercentile());
+		}
+		if (isEnabled(MetricAttribute.P999)) {
+			info.setP999(snapshot.get999thPercentile());
+		}
+
+		return info;
+	}
+
 	private TimerInfo getTimerInfo(String key, Timer timer) {
 		final Snapshot snapshot = timer.getSnapshot();
 		TimerInfo info = new TimerInfo();

+ 125 - 0
src/main/java/cn/com/qmth/examcloud/web/actuator/HistogramInfo.java

@@ -0,0 +1,125 @@
+package cn.com.qmth.examcloud.web.actuator;
+
+public class HistogramInfo {
+
+	private String key;
+
+	private Long count;
+
+	private double min;
+
+	private double max;
+
+	private double mean;
+
+	private String durationUnit;
+
+	private double p50;
+
+	private double p75;
+
+	private double p95;
+
+	private double p98;
+
+	private double p99;
+
+	private double p999;
+
+	public String getKey() {
+		return key;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public Long getCount() {
+		return count;
+	}
+
+	public void setCount(Long count) {
+		this.count = count;
+	}
+
+	public double getMin() {
+		return min;
+	}
+
+	public void setMin(double min) {
+		this.min = min;
+	}
+
+	public double getMax() {
+		return max;
+	}
+
+	public void setMax(double max) {
+		this.max = max;
+	}
+
+	public double getMean() {
+		return mean;
+	}
+
+	public void setMean(double mean) {
+		this.mean = mean;
+	}
+
+	public String getDurationUnit() {
+		return durationUnit;
+	}
+
+	public void setDurationUnit(String durationUnit) {
+		this.durationUnit = durationUnit;
+	}
+
+	public double getP50() {
+		return p50;
+	}
+
+	public void setP50(double p50) {
+		this.p50 = p50;
+	}
+
+	public double getP75() {
+		return p75;
+	}
+
+	public void setP75(double p75) {
+		this.p75 = p75;
+	}
+
+	public double getP95() {
+		return p95;
+	}
+
+	public void setP95(double p95) {
+		this.p95 = p95;
+	}
+
+	public double getP98() {
+		return p98;
+	}
+
+	public void setP98(double p98) {
+		this.p98 = p98;
+	}
+
+	public double getP99() {
+		return p99;
+	}
+
+	public void setP99(double p99) {
+		this.p99 = p99;
+	}
+
+	public double getP999() {
+		return p999;
+	}
+
+	public void setP999(double p999) {
+		this.p999 = p999;
+	}
+
+}

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

@@ -9,14 +9,6 @@ package cn.com.qmth.examcloud.web.actuator;
  */
 public enum MetricNames {
 
-	/**
-	 * 接口调用timer
-	 */
-	API_TIMER,
-
-	/**
-	 * 接口异常meter
-	 */
-	API_ERROR_METER
+	API_HISTOGRAM, API_METER, API_ERROR_METER
 
 }

+ 10 - 0
src/main/java/cn/com/qmth/examcloud/web/actuator/ReportInfo.java

@@ -5,12 +5,22 @@ import java.util.List;
 
 public class ReportInfo {
 
+	private List<HistogramInfo> histogramInfoList;
+
 	private List<MeterInfo> meterInfoList;
 
 	private List<TimerInfo> timerInfoList;
 
 	private Date dateTime;
 
+	public List<HistogramInfo> getHistogramInfoList() {
+		return histogramInfoList;
+	}
+
+	public void setHistogramInfoList(List<HistogramInfo> histogramInfoList) {
+		this.histogramInfoList = histogramInfoList;
+	}
+
 	public List<MeterInfo> getMeterInfoList() {
 		return meterInfoList;
 	}

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

@@ -1,6 +1,20 @@
 package cn.com.qmth.examcloud.web.actuator;
 
-public class TimerInfo extends MeterInfo {
+public class TimerInfo {
+
+	private String key;
+
+	private Long count;
+
+	private double meanRate;
+
+	private double oneMinuteRate;
+
+	private double fiveMinuteRate;
+
+	private double fifteenMinuteRate;
+
+	private String rateUnit;
 
 	private double min;
 
@@ -22,6 +36,62 @@ public class TimerInfo extends MeterInfo {
 
 	private double p999;
 
+	public String getKey() {
+		return key;
+	}
+
+	public void setKey(String key) {
+		this.key = key;
+	}
+
+	public Long getCount() {
+		return count;
+	}
+
+	public void setCount(Long count) {
+		this.count = count;
+	}
+
+	public double getMeanRate() {
+		return meanRate;
+	}
+
+	public void setMeanRate(double meanRate) {
+		this.meanRate = meanRate;
+	}
+
+	public double getOneMinuteRate() {
+		return oneMinuteRate;
+	}
+
+	public void setOneMinuteRate(double oneMinuteRate) {
+		this.oneMinuteRate = oneMinuteRate;
+	}
+
+	public double getFiveMinuteRate() {
+		return fiveMinuteRate;
+	}
+
+	public void setFiveMinuteRate(double fiveMinuteRate) {
+		this.fiveMinuteRate = fiveMinuteRate;
+	}
+
+	public double getFifteenMinuteRate() {
+		return fifteenMinuteRate;
+	}
+
+	public void setFifteenMinuteRate(double fifteenMinuteRate) {
+		this.fifteenMinuteRate = fifteenMinuteRate;
+	}
+
+	public String getRateUnit() {
+		return rateUnit;
+	}
+
+	public void setRateUnit(String rateUnit) {
+		this.rateUnit = rateUnit;
+	}
+
 	public double getMin() {
 		return min;
 	}

+ 4 - 4
src/main/java/cn/com/qmth/examcloud/web/enums/HttpServletRequestAttribute.java

@@ -45,13 +45,13 @@ public enum HttpServletRequestAttribute {
 	$_ENTERPRISE_ROOT_ORG_ID,
 
 	/**
-	 * metrics timer context
+	 * 接口调用异常
 	 */
-	$_METRICS_TIMER_CTX,
+	$_EXCEPTION_HAPPENED,
 
 	/**
-	 * 接口调用异常
+	 * ApiStatisticInterceptor 开始时间
 	 */
-	$_EXCEPTION_HAPPENED
+	API_STATISTIC_INTERCEPTOR_START_TIME,
 
 }

+ 27 - 19
src/main/java/cn/com/qmth/examcloud/web/interceptor/ApiStatisticInterceptor.java

@@ -5,10 +5,9 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.web.servlet.HandlerInterceptor;
 
+import com.codahale.metrics.Histogram;
 import com.codahale.metrics.Meter;
 import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.Timer;
-import com.codahale.metrics.Timer.Context;
 
 import cn.com.qmth.examcloud.web.actuator.MetricNames;
 import cn.com.qmth.examcloud.web.actuator.MetricRegistryHolder;
@@ -28,6 +27,10 @@ public class ApiStatisticInterceptor implements HandlerInterceptor {
 	public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
 			Object handler) throws Exception {
 
+		request.setAttribute(
+				HttpServletRequestAttribute.API_STATISTIC_INTERCEPTOR_START_TIME.name(),
+				System.currentTimeMillis());
+
 		ApiInfo apiInfo = (ApiInfo) request
 				.getAttribute(HttpServletRequestAttribute.$_API_INFO.name());
 
@@ -35,34 +38,39 @@ public class ApiStatisticInterceptor implements HandlerInterceptor {
 			return true;
 		}
 
-		Timer timer = MetricRegistryHolder.getDefalut()
-				.timer(MetricRegistry.name(MetricNames.API_TIMER.name(), apiInfo.getMapping()));
-		Context ctx = timer.time();
-		request.setAttribute(HttpServletRequestAttribute.$_METRICS_TIMER_CTX.name(), ctx);
-
 		return true;
 	}
 
 	@Override
 	public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
 			Object handler, Exception ex) throws Exception {
-		Context ctx = (Context) request
-				.getAttribute(HttpServletRequestAttribute.$_METRICS_TIMER_CTX.name());
-		if (null != ctx) {
-			ctx.stop();
+
+		ApiInfo apiInfo = (ApiInfo) request
+				.getAttribute(HttpServletRequestAttribute.$_API_INFO.name());
+
+		if (null == apiInfo) {
+			return;
 		}
+
+		Histogram histogram = MetricRegistryHolder.getDefalut().histogram(
+				MetricRegistry.name(MetricNames.API_HISTOGRAM.name(), apiInfo.getMapping()));
+
+		Long startTime = (Long) request.getAttribute(
+				HttpServletRequestAttribute.API_STATISTIC_INTERCEPTOR_START_TIME.name());
+
+		histogram.update(System.currentTimeMillis() - startTime);
+
 		Boolean hasException = (Boolean) request
 				.getAttribute(HttpServletRequestAttribute.$_EXCEPTION_HAPPENED.name());
 
+		Meter apiMeter = MetricRegistryHolder.getDefalut()
+				.meter(MetricRegistry.name(MetricNames.API_METER.name(), apiInfo.getMapping()));
+		apiMeter.mark();
+
 		if (null != hasException && hasException) {
-			ApiInfo apiInfo = (ApiInfo) request
-					.getAttribute(HttpServletRequestAttribute.$_API_INFO.name());
-
-			if (null != apiInfo) {
-				Meter meter = MetricRegistryHolder.getDefalut().meter(MetricRegistry
-						.name(MetricNames.API_ERROR_METER.name(), apiInfo.getMapping()));
-				meter.mark();
-			}
+			Meter apiErrorMeter = MetricRegistryHolder.getDefalut().meter(
+					MetricRegistry.name(MetricNames.API_ERROR_METER.name(), apiInfo.getMapping()));
+			apiErrorMeter.mark();
 		}
 
 	}

+ 4 - 12
src/main/resources/api-status.html

@@ -34,7 +34,7 @@ td {
 			<th colspan="3">cost (ms)</th>
 			<th colspan="5">Line (<= ms)</th>
 			<th rowspan="2" onclick="order('exceptionCount')">error</th>
-			<th colspan="5">error percent (%)</th>
+			<th rowspan="2">error %</th>
 		</tr>
 		<tr style="background-color: #E0E0E0;">
 			<th onclick="order('meanRate')">mean</th>
@@ -49,15 +49,10 @@ td {
 			<th onclick="order('p95')">95%</th>
 			<th onclick="order('p98')">98%</th>
 			<th onclick="order('p99')">99%</th>
-			<th onclick="order('exceptionMeanPercent')">mean</th>
-			<th onclick="order('exceptionOneMinutePercent')">1-min</th>
-			<th onclick="order('exceptionFiveMinutePercent')">5-min</th>
-			<th onclick="order('exceptionFifteenMinutePercent')">15-min</th>
 		</tr>
 		<#list data as cur>
 		<tr style="color: #009999;">
-			<td>${cur.mapping?replace("][","]<br>[","f")}
-			</td>
+			<td>${cur.mapping!}</td>
 			<td>${cur.description!}</td>
 			<td>${cur.count?c}</td>
 			<td>${cur.meanRate?string("#.##")}</td>
@@ -72,11 +67,8 @@ td {
 			<td>${cur.p95?string("#.##")}</td>
 			<td>${cur.p98?string("#.##")}</td>
 			<td>${cur.p99?string("#.##")}</td>
-			<td>${cur.exceptionCount?c}</td>
-			<td>${cur.exceptionMeanPercent?string.percent}</td>
-			<td>${cur.exceptionOneMinutePercent?string.percent}</td>
-			<td>${cur.exceptionFiveMinutePercent?string.percent}</td>
-			<td>${cur.exceptionFifteenMinutePercent?string.percent}</td>
+			<td>${cur.errorCount?c}</td>
+			<td>${cur.errorPercent?string.percent}</td>
 		</tr>
 		</#list>
 	</table>