序
对于系统服务的监控,tcp层通常是用heartbeat来进行,最简单的比如ping-pong。对于http层,来说springboot的actuator内置了/health的endpoint,很方便地规范了每个服务的健康状况的api,而且HealthIndicator可以自己去扩展,增加相关依赖服务的健康状态,非常灵活方便而且可扩展。
/health实例
{ "status": "UP", "custom": { "status": "UNKNOWN", "custom": { "status": "UNKNOWN", "msg": "mock down to test aggregator" } }, "diskSpace": { "status": "UP", "total": 249779191808, "free": 57925111808, "threshold": 10485760 }}
status枚举
/** * Convenient constant value representing unknown state. */ public static final Status UNKNOWN = new Status("UNKNOWN"); /** * Convenient constant value representing up state. */ public static final Status UP = new Status("UP"); /** * Convenient constant value representing down state. */ public static final Status DOWN = new Status("DOWN"); /** * Convenient constant value representing out-of-service state. */ public static final Status OUT_OF_SERVICE = new Status("OUT_OF_SERVICE");
/health的两个贴心点
对于多个HealthIndicator的status,spring boot默认对其进行aggregrate,然后计算最顶层的status字段的值,而且对于status是DOWN或者是OUT_OF_SERVICE的,返回的http的状态码是503,这对于应用监控系统来说真是大大的贴心啊,再总结一下:
自动聚合多个HealthIndicator的status
对于status是DOWN或者是OUT_OF_SERVICE的,返回503
这样应用监控系统一来就无需去解析返回结果,直接根据http的状态码就可以判断了,非常方便,太省心了有没有。
HealthAggregator
/** * Base {@link HealthAggregator} implementation to allow subclasses to focus on * aggregating the {@link Status} instances and not deal with contextual details etc. * * @author Christian Dupuis * @author Vedran Pavic * @since 1.1.0 */public abstract class AbstractHealthAggregator implements HealthAggregator { @Override public final Health aggregate(Maphealths) { List statusCandidates = new ArrayList (); for (Map.Entry entry : healths.entrySet()) { statusCandidates.add(entry.getValue().getStatus()); } Status status = aggregateStatus(statusCandidates); Map details = aggregateDetails(healths); return new Health.Builder(status, details).build(); } /** * Return the single 'aggregate' status that should be used from the specified * candidates. * @param candidates the candidates * @return a single status */ protected abstract Status aggregateStatus(List candidates); /** * Return the map of 'aggregate' details that should be used from the specified * healths. * @param healths the health instances to aggregate * @return a map of details * @since 1.3.1 */ protected Map aggregateDetails(Map healths) { return new LinkedHashMap (healths); }}
OrderedHealthAggregator#aggregateStatus
@Override protected Status aggregateStatus(Listcandidates) { // Only sort those status instances that we know about List filteredCandidates = new ArrayList (); for (Status candidate : candidates) { if (this.statusOrder.contains(candidate.getCode())) { filteredCandidates.add(candidate); } } // If no status is given return UNKNOWN if (filteredCandidates.isEmpty()) { return Status.UNKNOWN; } // Sort given Status instances by configured order Collections.sort(filteredCandidates, new StatusComparator(this.statusOrder)); return filteredCandidates.get(0); }
可以看出是对status进行排序,然后取第一个的状态,其中statusOrder如下:
private ListstatusOrder; /** * Create a new {@link OrderedHealthAggregator} instance. */ public OrderedHealthAggregator() { setStatusOrder(Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN); }
排序方法
/** * {@link Comparator} used to order {@link Status}. */ private class StatusComparator implements Comparator{ private final List statusOrder; StatusComparator(List statusOrder) { this.statusOrder = statusOrder; } @Override public int compare(Status s1, Status s2) { int i1 = this.statusOrder.indexOf(s1.getCode()); int i2 = this.statusOrder.indexOf(s2.getCode()); return (i1 < i2 ? -1 : (i1 == i2 ? s1.getCode().compareTo(s2.getCode()) : 1)); } }
即Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN优先级依次递减。status中一旦有出现DOWN的情况,整体的status就是DOWN,依次类推。