前言
我们在上一篇文章中,讲解了协调者启动的总体流程,也讲解了参数解析的实现。这一篇文章中,我们就要讲第二步,metrics的初始化。首先我们会介绍一下metrics初始化的过程,然后直接把整个metrics
模块讲清楚。内容较多。
初始化metrics
metrics的初始化,使用了MetricsManager
。我们常见的"懒汉单例"初始化方式有:枚举、双检锁、静态内部类等。MetricsManager
使用了”懒汉版“的单例模式,并且用了静态内部类的方式做了单例的初始化。
初始化metric的步骤如下:
- 从配置中心读取配置,看是否需要初始化metric
- 通过
RegistryFactory
初始化Registry对象 - 然后使用Exporter设置Registry对象
- 最后用EventBusManager注册一个metrics的订阅
Registry是什么?
它定义了getCounter
、getSummary
、getTimer
等接口。进行具体的metrics统计。
Exporter是什么?
发布器,把度量数据同步给对应的监控系统,如:Prometheus
。Exporter可能同时存在多个。
EventBusManager又是什么?
metrics的数据来源就是通过订阅EventBus获取的。
一起来看看源码,不要太简单:
public void init() {
//读取配置看是否要开启
boolean enabled = ConfigurationFactory.getInstance().getBoolean(
ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, false);
if (enabled) {
//生成registry
registry = RegistryFactory.getInstance();
if (registry != null) {
//获取Exporter
List<Exporter> exporters = ExporterFactory.getInstanceList();
if (exporters.size() != 0) {
exporters.forEach(exporter -> exporter.setRegistry(registry));
//注册MetricsSubscriber到EventBus
EventBusManager.get().register(new MetricsSubscriber(registry));
}
}
}
}
metrics模块的实现原理
先来看一把总体流程
当状态有变化,EvenBus会把事件推送给MetricsSubscriber,MetricsSubscriber中调用Registry把度量数据写入。Exporter再定期把度量数据拉出来,发给外部监控系统。
由于涉及到配置的读取,我们在下篇文章在撸这块,本篇先把metrics模块搞定。
Metric模块
这个模块主要分为以下几部分:
- seata-metrics-api
- seata-metrics-core
- seata-metrics-exporter-prometheus
- seata-metrics-registry-compact
api不用做过多的解释了,接口类丢这里面就对了。
seata-metrics-core,看名字也知道是核心类,主要定义了exporter的工厂类和类型,因为exporter可能会有多种实现。因此需要工厂类来初始化,另外需要类型来区分不同的exporter。另外就是定义了registry的工厂类和类型。原因和exporter一样。
seata-metrics-exporter-prometheus是exporter的prometheus实现,相信后面还有各种seata-metrics-exporter-xxxxx。它的作用就是将度量数据同步给 Prometheus。
seata-metrics-registry-compact同理是registry的一种实现,
seata-metrics-core
先看看ExporterFactory和ExporterType的实现吧,也好简单:
public static List<Exporter> getInstanceList() {
List<Exporter> exporters = new ArrayList<>();
//从配置中心获取要初始化哪些exporter
String exporterTypeNameList = ConfigurationFactory.getInstance().getConfig(
ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_EXPORTER_LIST, null);
if (!StringUtils.isNullOrEmpty(exporterTypeNameList)) {
//配置中多个exporter肯定是用英文逗号隔开的
String[] exporterTypeNames = exporterTypeNameList.split(",");
for (String exporterTypeName : exporterTypeNames) {
ExporterType exporterType;
try {
//得到exporterType
exporterType = ExporterType.getType(exporterTypeName);
//通过扩展点加载exporter,并保存在exporters中
exporters.add(
EnhancedServiceLoader.load(Exporter.class, Objects.requireNonNull(exporterType