micrometer系列1:Meter和MeterRegistry

了解micrometer中的Meter和MeterRegistry。

micrometer源码分析之Meter和MeterRegistry。

Meter

Meter是整个micrometer指标的基础接口。

micrometer-metric-type-hierarchy.png

一个Meter包含的核心成员有Id、Measurement

Meter.png

Id

Id包含名字、标签列表、Meter类型、基础单位等属性。 留意这里有个syntheticAssociation属性,用于记录当前Meter从哪个Meter衍生出来。

在micrometer中,tag和其他监控系统中的dimension是相同的概念。 支持tag的好处就是可以进行多维度的统计和查询。例如把服务作为一个tag,主机作为另一个tag,就可以观察到一个服务在不同主机的运行情况。

class Id {
    private final String name;
    private final Tags tags;
    private final Type type;
    @Nullable
    /**
     * For internal use. Indicates that this Id is tied to a meter that is a derivative of another metric.
     * For example, percentiles and histogram gauges generated by {@link HistogramGauges} are derivatives
     * of a {@link Timer} or {@link DistributionSummary}.
     * <p>
     * This method may be removed in future minor or major releases if we find a way to mark derivatives in a
     * private way that does not have other API compatibility consequences.
     *
     * @return The meter id of a meter for which this metric is a synthetic derivative.
     */    
    private final Meter.Id syntheticAssociation;
    @Nullable
    private final String description;
    @Nullable
    private final String baseUnit;
}

Measurement

Measurement包含了测量Meter的方式。 其中测量值抽象为Supplier<Double> valueFunction,方便提供不同的实现。

public class Measurement {
    private final Supplier<Double> f;
    private final Statistic statistic;

    public Measurement(Supplier<Double> valueFunction, Statistic statistic) {
        this.f = valueFunction;
        this.statistic = statistic;
    }
    // more code
}

Statistic是对value类型描述的枚举类型。

public enum Statistic {
    /**
     * The sum of the amounts recorded.
     */
    TOTAL("total"),

    /**
     * The sum of the times recorded. Reported in the monitoring system's base unit of time
     */
    TOTAL_TIME("total"),

    /**
     * Rate per second for calls.
     */
    COUNT("count"),

    /**
     * The maximum amount recorded. When this represents a time, it is reported in the monitoring system's base unit of time.
     */
    MAX("max"),

    /**
     * Instantaneous value, such as those reported by gauges.
     */
    VALUE("value"),

    /**
     * Undetermined.
     */
    UNKNOWN("unknown"),

    /**
     * Number of currently active tasks for a long task timer.
     */
    ACTIVE_TASKS("active"),

    /**
     * Duration of a running task in a long task timer. Always reported in the monitoring system's base unit of time.
     */
    DURATION("duration");

    private final String tagValueRepresentation;

    Statistic(String tagValueRepresentation) {
        this.tagValueRepresentation = tagValueRepresentation;
    }

    public String getTagValueRepresentation() {
        return tagValueRepresentation;
    }
}

Builder

因为可配置属性多,Meter使用了builder设计模式,提供Builder内部类,方便自定义构建Meter。

Type

Type枚举包含具体的metris类型,以后会分别探讨。

enum Type {
    COUNTER,
    GAUGE,
    LONG_TASK_TIMER,
    TIMER,
    DISTRIBUTION_SUMMARY,
    OTHER;
}

MeterRegistry

MeterRegistry.png

MeterRegistry是注册Meter的基类实现。MeterRegistry包含了

  • Clock。用于测量持续时间。
  • MeterFilter。过滤Meter的条件,还提供重命名、tag改名、修改tag value等功能。
  • Meter监听机制(meterAddedListeners、meterRemovedListeners)。
  • 配置工具Config。
  • More类,提供不常用metrics的工具方法,例如FunctionCounter、LongTaskTimer等。
  • PauseDetector,检测暂停,以后再讨论。
  • syntheticAssociations 保存了meter之间的衍生关系。
  • NamingConvention,用于转换不同底层监控系统的命名规则。
public abstract class MeterRegistry {
    protected final Clock clock;
    private final Object meterMapLock = new Object();
    private volatile MeterFilter[] filters = new MeterFilter[0];
    private final List<Consumer<Meter>> meterAddedListeners = new CopyOnWriteArrayList<>();
    private final List<Consumer<Meter>> meterRemovedListeners = new CopyOnWriteArrayList<>();
    private final Config config = new Config();
    private final More more = new More();

    private volatile PMap<Id, Meter> meterMap = HashTreePMap.empty();

    /**
     * Map of meter id whose associated meter contains synthetic counterparts to those synthetic ids.
     * We maintain these associations so that when we remove a meter with synthetics, they can removed
     * as well.
     */
    private volatile PMap<Id, PSet<Id>> syntheticAssociations = HashTreePMap.empty();

    private final AtomicBoolean closed = new AtomicBoolean(false);
    private PauseDetector pauseDetector = new NoPauseDetector();

    /**
     * We'll use snake case as a general-purpose default for registries because it is the most
     * likely to result in a portable name. Camel casing is also perfectly acceptable. '-' and '.'
     * separators can pose problems for some monitoring systems. '-' is interpreted as metric
     * subtraction in some (including Prometheus), and '.' is used to flatten tags into hierarchical
     * names when shipping metrics to hierarchical backends such as Graphite.
     */
    private NamingConvention namingConvention = NamingConvention.snakeCase;

    // more code
}

不同的监控系统,通过实现各自的MeterRegistry,对接到micrometer。 micrometer也提供了2个基础MeterRegistry的实现。

SimpleMeterRegistry

SimpleMeterRegistry是一个in-memory的实现。它不会对外暴露metrics。适合于简单测试。

CompositeMeterRegistry

用于组合多个不同的监控系统。这样一个meter采集的数据可以发送到不同的监控系统。

把meter添加到各个registry,是一个可能发生并发但是低频的操作。在实现上,CompositeMeterRegistry使用CAS乐观锁提高性能。

public class CompositeMeterRegistry extends MeterRegistry {
    private final AtomicBoolean registriesLock = new AtomicBoolean(false);
    private final Set<MeterRegistry> registries = Collections.newSetFromMap(new IdentityHashMap<>());

    private void lock(AtomicBoolean lock, Runnable r) {
        for (; ; ) {
            // CAS操作
            if (lock.compareAndSet(false, true)) {
                try {
                    r.run();
                    break;
                } finally {
                    lock.set(false);
                }
            }
        }
    }

    public CompositeMeterRegistry(Clock clock, Iterable<MeterRegistry> registries) {
        super(clock);
        config()
                .namingConvention(NamingConvention.identity)
                .onMeterAdded(m -> {
                    if (m instanceof CompositeMeter) { // should always be
                        lock(registriesLock, () -> nonCompositeDescendants.forEach(((CompositeMeter) m)::add));
                    }
                })
                .onMeterRemoved(m -> {
                    if (m instanceof CompositeMeter) { // should always be
                        lock(registriesLock, () -> nonCompositeDescendants.forEach(r -> r.remove(m)));
                    }
                });

        registries.forEach(this::add);
    }

}

CompositeMeterRegistry注册的meter都是AbstractCompositeMeter的子类。

AbstractCompositeMeter.png

abstract class AbstractCompositeMeter<T extends Meter> extends AbstractMeter implements CompositeMeter {
    private AtomicBoolean childrenGuard = new AtomicBoolean();
    private Map<MeterRegistry, T> children = Collections.emptyMap();

    // There are no child meters at the moment. Return a lazily instantiated no-op meter.
    @Nullable
    private volatile T noopMeter;

    AbstractCompositeMeter(Id id) {
        super(id);
    }

    abstract T newNoopMeter();

    @Nullable
    abstract T registerNewMeter(MeterRegistry registry);

    // more code
}

子类需要实现newNoopMeter和registerNewMeter方法。 和CompositeMeterRegistry类似,AbstractCompositeMeter也是使用CAS方式解决并发安全问题。

PrometheusMeterRegistry

PrometheusMeterRegistry的scrape()对外暴露Prometheus指标。

public String scrape() {
    Writer writer = new StringWriter();
    try {
        scrape(writer);
    } catch (IOException e) {
        // This actually never happens since StringWriter::write() doesn't throw any IOException
        throw new RuntimeException(e);
    }
    return writer.toString();
}

public void scrape(Writer writer) throws IOException {
    TextFormat.write004(writer, registry.metricFamilySamples());
}

io.prometheus.client.exporter.common.TextFormat根据version 0.0.4暴露Prometheus指标。prometheus指标协议具体见EXPOSITION FORMATS

# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0.001996
go_gc_duration_seconds_sum 0.0039907
go_gc_duration_seconds_count 48

Metrics

Metrics是一个工具类,提供了全局静态meter注册器(是一个CompositeMeterRegistry)。 另外包含一个内部类More,提供不常用的meter类型,例如LongTaskTimer、FunctionCounter等。

public class Metrics {
    public static final CompositeMeterRegistry globalRegistry = new CompositeMeterRegistry();
    private static final More more = new More();
    // more
}
Built with Hugo
Theme Stack designed by Jimmy