概述
Exporter是Prometheus监控系统中一个重要组件,可以理解为收集监控对象各种数据的agent。Prometheus为我们提供了较多的官方及第三方export,如果想要实现用户自己应用的监控,则需自己开发exporter,常用的监控对象大致可分为数据库对象,中间件对象,主机及操作系统,以及用户应用等。其中本文主要介绍了国产数据库gbase-8a的通用模块
(1)添加依赖
首先在国产数据库项目(例如exporter-gbase-8a)下的pom.xml中添加exporter-select-sql的依赖,用于让国产数据库项目与exporter-select-sql的jar包相关联
<dependency><groupId>com.apusic.amp.exporter</groupId><artifactId>exporter-select-sql</artifactId><version>0.0.1-SNAPSHOT</version></dependency>
(2)注册
在国产数据库项目(例如exporter-gbase-8a)下的application注册文件下(例如Gbase8aExporterApplication)注册selectSqlExporter和gbase8aExporter采集器,注册方式为
@SpringBootApplication
@EnablePrometheusEndpoint
@ComponentScan("com.apusic.amp.exporter")
public class Gbase8aExporterApplication extends WebMvcConfigurerAdapter implements CommandLineRunner {@Autowiredprivate SelectSqlExporter selectSqlExporter;@Autowiredprivate Gbase8aExporter gbase8aExporter;public static void main(String[] args) {SpringApplication.run(Gbase8aExporterApplication.class, args);}@Overridepublic void run(String... args) throws Exception {DefaultExports.initialize();selectSqlExporter.register();gbase8aExporter.register();}
}
(3)根据不同国产数据库的需求配置metrics.yml
找到国产数据库项目下的metrics.yml配置文件(例如exporter-gbase-8a项目下的metrics_select_sql.yml)
因为exporter后的metrics内容格式是由context+name+fieldForValue+help+type+fieldForLabels组成,所以通过metrics_select_sql.yml配置文件依次获取到各项指标的值,然后根据Prometheus所需的格式排列起来,例如
# 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
其中metrics_select_sql.yml的内容
selectmetrics:dbVersion: 8.6 //gbase_8a的版本为8.6dbName: gbase_8a //所采集的数据库名称(第一个拼接值,可以自己设置)metricFamilies: //一条sql语句对应的metric集合- context: info_schema //标签名(第二个拼接值,可以自己设置)sql: SELECT TABLE_SCHEMA as tableschema,TABLE_NAME as tablename,TABLE_TYPE as type,ifnull(ENGINE, 'NONE') as engine,ifnull(VERSION, '0') as version,ifnull(ROW_FORMAT, 'NONE') as row_format,ifnull(TABLE_ROWS, '0') as TABLE_ROWS,ifnull(DATA_LENGTH, '0') as DATA_LENGTH,ifnull(INDEX_LENGTH, '0') as INDEX_LENGTH,ifnull(DATA_FREE, '0') as DATA_FREE,ifnull(CREATE_OPTIONS, '') as create_options FROM information_schema.tables //select语句metrics: //sql语句查询到的结果集合- name: table_version //字段名(第三个拼接值,通常为字段名,也可以自己设置容易理解的内容)fieldForValue: version //字段值(第五个拼接值,所需要的指标的值,固定写法,通常为字段名)help: "The version number of the table's .frm file" //解释type: gauge //指标类型fieldForLabels: //标签(第四个拼接值,在{}中,每个标签用逗号隔开,通常设置一些固定的内容,比如名字,id等信息)- tableschema- tablename- type- engine- row_format- create_options
(4)在exporter-select-sql配置项目下的SelectSqlMetricsYmlLoader中读取配置文件
SelectSqlMetricsYmlLoader的内容
@Component
@PropertySource(value = {"${metrics.selectsql.config}"}, factory = SelectSqlMetricsYmlPropertyFactory.class)
public class SelectSqlMetricsYmlLoader {
}
(5)在exporter-select-sql配置项目下的SelectSqlMetricsService
其中SelectSqlMetricsService的步骤分别为
1.首先在metrics第一次初始化的时候,会执行一次监控的国产数据库的版本判定,版本判断通过后执行下一步
2.接下来从metrics集合中获取sql语句所查询到的指标内容,并判断指标是否有固定名称,然后对指标的各项内容进行拼接,拼接成Prometheus所需的格式并用-排列起来,排列方式为context-name-fieldForValue-help-type-fieldForLabels,最后可以添加文件名缓存,如果文件前面的名称相同,可以直接读取缓存的名称。
SelectSqlMetricsService源码如下
@Service
public class SelectSqlMetricsService {@Autowiredprivate SelectSqlMetricYmlProperty dataBaseMetricProperty;@Autowiredprivate SelectSqlMetricsDao dmActivityMetricsDao;private boolean isCheckEnable;static final Gauge inprogressRequests = Gauge.build().name("db_up").help("Whether the database server is up.").register();private static final Logger logger = LoggerFactory.getLogger(SelectSqlMetricsService.class);public void collect(List<Collector.MetricFamilySamples> metricsList) {Double status = dmActivityMetricsDao.status();inprogressRequests.set(status);if (status < 1) {return;}if (!isCheckEnable) {checkMetricFamilyEnable();}for (SelectSqlMetricFamily metricFamily : dataBaseMetricProperty.getMetricFamilies()) {if (!metricFamily.isEnable()) {continue;}List<Map<String, String>> resultMapList = null;try {resultMapList = dmActivityMetricsDao.query(metricFamily.getSql());} catch (Exception e) {logger.error("Get Metrics ResultMap Error! Metrics: " + metricFamily.getContext() + ", Detail: " + e.getMessage());continue;}if(resultMapList == null || resultMapList.size() == 0) {continue;}//初始化一个map来放 MetricFamilySamplesMap<String, Collector.MetricFamilySamples> metricFamilySamplesMap = new HashMap<>();for (SelectSqlMetric metric : metricFamily.getMetrics()) {for (Map<String, String> resultMap : resultMapList) {//当指标值指定的列的值不能解析成Double时,跳过Double value = null;try {value = Double.parseDouble(resultMap.get(metric.getFieldForValue()));} catch (Exception e) {continue;}//获取labelList<String> labelKeys = new ArrayList<>();List<String> labelValues = new ArrayList<>();if (metric.getFieldForLabels() != null) {for (String label : metric.getFieldForLabels()) {labelKeys.add(label);labelValues.add(resultMap.get(label).trim());}}if (metric.getConstantLabels() != null) {for (Map.Entry<String, String> entry : metric.getConstantLabels().entrySet()) {labelKeys.add(entry.getKey());labelValues.add(entry.getValue());}}//获取nameString metricName = metric.getName() != null ? metric.getName() : "";if (StringUtils.isEmpty(metric.getFieldForName())) {metricName = buildMetricName(dataBaseMetricProperty.getDbName(), metricFamily.getContext(), metricName);} else {metricName = buildMetricName(dataBaseMetricProperty.getDbName(), metricFamily.getContext(), cleanName(resultMap.get(metric.getFieldForName())));}//创建指标MetricFamilyFactory.createMetricFamilyUseingCache(metricFamilySamplesMap, metricName, metric.getHelp(), metric.getType(), labelKeys, labelValues, value);}}metricsList.addAll(metricFamilySamplesMap.values());}}private String buildMetricName(String namespace, String context, String metricName) {if (StringUtils.isEmpty(metricName)) {return "";}if (!StringUtils.isEmpty(namespace) && !StringUtils.isEmpty(context)) {return namespace + "_" + context + "_" + metricName;} else if (!StringUtils.isEmpty(namespace)) {return namespace + "_" + metricName;} else if (!StringUtils.isEmpty(context)) {return context + "_" + metricName;} else {return metricName;}}//清除指标名称中的空格,/private String cleanName(String s) {s = s.trim();s = s.replaceAll(" ", "_");s = s.replaceAll("/", "");return s;}private MetricsType getMetricType(String metricType) {MetricsType type = null;try {type = MetricsType.valueOf(metricType.toUpperCase());} catch (Exception e) {throw new RuntimeException("No metrics found while parsing: " + metricType);}return type;}/*** 根据版本判断指标族是否可用*/private void checkMetricFamilyEnable() {for (SelectSqlMetricFamily metricFamily : dataBaseMetricProperty.getMetricFamilies()) {//未申明任何版本号,默认开启该指标族if (dataBaseMetricProperty.getDbVersion() >= metricFamily.getLowVersion() && (metricFamily.getUpVersion() <= 0 || dataBaseMetricProperty.getDbVersion() <= metricFamily.getUpVersion())) {metricFamily.setEnable(true);} else {metricFamily.setEnable(false);}}isCheckEnable = true;}
}