需求场景
当我们使用Spring Boot Actuator对Spring项目进行监控时,我们可以得到大部分我们想要的监控数据,但是仍无法满足我们的监控场景需要。
当前需要监控的场景之一是,在项目中通过RestTemplate调用远程接口时,监控调用的返回状态,以监控远程服务的状态。在一时间段内,若状态 > 400的次数过多,意味着远程服务出现问题,应当采取紧急维护措施。在Spring Boot 1.5.X中,并没有集成对RestTemplate的监控。因此,通过参考Spring文档中52.5 Recording your own metrics的方法,实现当前所需场景的监控。
实现方法
1. 创建HTTP Request拦截器
|
其中,
counterService.increment(String.format("counter.status.code.%d-%s", statusCode.value(), request.getURI().getHost())); |
对RestTemplate的返回结果状态进行计数,并将计数保存在内存中。
2. 将HTTP返回状态计数加入到/health中
查看counterService.increment()的源码,可以看到,
DropwizardMetricServices.classpublic class DropwizardMetricServices implements CounterService, GaugeService {
private final MetricRegistry registry;
...
...
private void incrementInternal(String name, long value) {
if (name.startsWith("meter")) {
Meter meter = this.registry.meter(name);
meter.mark(value);
} else {
name = this.wrapCounterName(name);
Counter counter = this.registry.counter(name);
counter.inc(value);
}
}
...
...
}
MetricRegistry.classpublic class MetricRegistry implements MetricSet {
...
...
public Counter counter(String name) {
return (Counter)this.getOrAdd(name, MetricRegistry.MetricBuilder.COUNTERS);
}
private <T extends Metric> T getOrAdd(String name, MetricRegistry.MetricBuilder<T> builder) {
Metric metric = (Metric)this.metrics.get(name);
if (builder.isInstance(metric)) {
return metric;
} else {
if (metric == null) {
try {
return this.register(name, builder.newMetric());
} catch (IllegalArgumentException var6) {
Metric added = (Metric)this.metrics.get(name);
if (builder.isInstance(added)) {
return added;
}
}
}
throw new IllegalArgumentException(name + " is already used for a different type of metric");
}
}
public <T extends Metric> T register(String name, T metric) throws IllegalArgumentException {
if (metric instanceof MetricSet) {
this.registerAll(name, (MetricSet)metric);
} else {
Metric existing = (Metric)this.metrics.putIfAbsent(name, metric);
if (existing != null) {
throw new IllegalArgumentException("A metric named " + name + " already exists");
}
this.onMetricAdded(name, metric);
}
return metric;
}
...
...
}
自定义的计数器将在注册到MetricRegistry容器中,因此,在后续对计数的操作,都可以从MetricRegistry中获取数据进行操作。
构建过滤器,获取计数
要想批量获取MetricRegistry的metric信息,可以通过MetricFilter对Metric的key进行过滤。
private final static MetricFilter metricFilter = new MetricFilter() {
public boolean matches(String s, Metric metric) {
if (s.startsWith("counter.status.code")) return true;
return false;
}
};提取Metric供健康检查(/health)使用
Map metricCounters = metricRegistry.getCounters(metricFilter);
http://127.0.0.1:8088/health{
"status": "UP",
"remoteHttpStatus": {
"status": "UP",
"detail": {
"comment.social.3g.net.cn": {
"counter.status.code.200": 229
}
},
"failedCount": 0
}
}