对于 java 应用监控,通常我们需要考虑三个方面问题:
接下来我将对这三个方面进行解释,这个是我自己的思考,一定存在很多局限性,欢迎找我交流。
这里系统 metic 监控主要指的 jvm 指标信息,当然如果你使用 spring 框架,还会有更多的信息。暂且将类需求都归属于系统 metric 中。目前主流的做法:开启 jmx 端口,收集 jmx 信息。如果是 spring,可以使用 actualor。
服务出现问题,首先会按照我们的代码设计产生不同级别的日志,我们需要将自己关心的数据能够自动推送给我们。最简单的办法是配置 log 发邮件的功能。一个是我们能够收到日志的告警,一个是能便捷高效的查看错误日志。
我接触过的方案有查看容器 log,或者使用 filbeat 采集应用的 log,借助 ELK,实现日志检索的能力。
业务指标更多的是关注的业务性能,比如 api 的平均响应延迟,吞吐量,数据库响应延迟等不同维度的监控数据。
对于这里的需求,我会更多的借助于第三方框架,例如:elastic APM、Prometheus、SkyWalking
Spring Boot 应用监控有很多方案,例如 elastic APM,Prometheus 等。各有特色,本次实践采用方案:Micrometer+Prometheus+Grafana。
选择 Micrometer 最重要的原因是他的设计很灵活,并且和 spring boot 2.x 集成度很高。对于 jvm 的监控很容易集成,难度很小。本次实践包含 jvm 监控和业务性能指标监控。
搭建 promethues
docker run \-p 9090:9090 \--name prometheus-v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml \prom/prometheus
global:scrape_interval: 15s # By default, scrape targets every 15 seconds.evaluation_interval: 15s # By default, scrape targets every 15 seconds.# scrape_timeout is set to the global default (10s).# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.rule_files:# - "first.rules"# - "second.rules"# A scrape configuration containing exactly one endpoint to scrape:# Here it's Prometheus itself.scrape_configs:# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.- job_name: 'demo_platform'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5smetrics_path: '/actuator/prometheus'# scheme defaults to 'http'.static_configs:- targets: ['127.0.0.1:8080']
搭建 grafana
docker run -d -p 3000:3000 --name grafana grafana/grafana:6.5.0
Micrometer(译:千分尺) Micrometer provides a simple facade over the instrumentation clients for the most popular monitoring systems. 翻译过来大概就它提供一个门面,类似 SLF4j。支持将数据写入到很多监控系统,不过我谷歌下来,很多都是后端接入的是 Prometheus.
Micrometer 提供了与供应商无关的接口,包括 timers(计时器), gauges(量规), counters(计数器), distribution summaries(分布式摘要), long task timers(长任务定时器)。它具有维度数据模型,当与维度监视系统结合使用时,可以高效地访问特定的命名度量,并能够跨维度深入研究。
支持的监控系统:AppOptics , Azure Monitor , Netflix Atlas , CloudWatch , Datadog , Dynatrace , Elastic , Ganglia , Graphite , Humio , Influx/Telegraf , JMX , KairosDB , New Relic , Prometheus , SignalFx , Google Stackdriver , StatsD , Wavefront
Meter
是指一组用于收集应用中的度量数据的接口,Meter 单词可以翻译为"米"或者"千分尺",但是显然听起来都不是很合理,因此下文直接叫 Meter,理解它为度量接口即可。Meter
是由MeterRegistry
创建和保存的,可以理解MeterRegistry
是Meter
的工厂和缓存中心,一般而言每个 JVM 应用在使用 Micrometer 的时候必须创建一个MeterRegistry
的具体实现。Micrometer 中,Meter
的具体类型包括:Timer
,Counter
,Gauge
,DistributionSummary
,LongTaskTimer
,FunctionCounter
,FunctionTimer
和TimeGauge
。一个Meter
具体类型需要通过名字和Tag
(这里指的是 Micrometer 提供的 Tag 接口)作为它的唯一标识,这样做的好处是可以使用名字进行标记,通过不同的Tag
去区分多种维度进行数据统计。
与 spring boot 集成,这里的 metric 主要是由 spring actuator 提供
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId></dependency>
management:endpoint:health:enabled: falseendpoints:web:exposure:include: '*'exclude: env,beansmetrics:enable:http: falsehikaricp: false
这里有几个注意的点management.endpoint.health.enabled
只是为了禁用 spring 默认的健康检查,非必须。exclude: env,beans
也不需要配置,只是在我项目中为了减少导出的 metric。同理management.metrics.enable
也是为了减少收集的数据,使用方法为你定义指标的前缀。
只有management.endpoints.web.exposure.include
为必须的,这里也只是为了导出/actuator/prometheus
,通过该地址可以访问到响应的 metric 信息。
访问 http://localhost:8080/actuator/prometheus
即可看到响应的 metric 信息。
在 grafana 中中导入JVM (Micrometer)
即可看到如下效果:
因为系统遗留监控代码的原因,这里采用的是全局静态方法实现。
protected static Iterable<Tag> tags(String service, String category, String method) {return Tags.of("service", service, "category", category, "method", method);}protected static Iterable<Tag> tags(String service, String category) {return Tags.of("service", service, "category", category);}public static void controllerMetric(String service, MonitorMetric.MonitorOperationType type, String method, long time) {try {Metrics.counter(Constants.HTTP_REQUESTS_TOTAL, tags(service, type.name(), method)).increment();Metrics.timer(Constants.REQUESTS_LATENCY, tags(service, type.name())).record(Duration.ofMillis(time));} catch (Exception e) {e.printStackTrace();}}
解释一下,这里可以统计出请求数和请求延迟。
对于每秒请求数据量,可以使用increase(http_requests_total{job=~"$job",instance=~"$instance"}[1m])
对于平均请求延迟,可以使用rate(timer_sum[1m])/rate(timer_count[1m])
对于 Throughput 可以使用rate(timer_count[1m])
Percentile histograms与Distribution summaries性能损失还无法确定,不过查看PrometheusTimer
,结合测试,还是有一定的性能损失,不过这里未深入研究。
可以在定义静态方法类,初始化的时候做一点配置,registry 可以使用 spring 注入进来例如:
@AutowiredMeterRegistry registry;
public MonitorMetric(MeterRegistry registry) {registry.config().meterFilter(new MeterFilter() {@Overridepublic DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {if (id.getName().startsWith("requests_latency")) {return DistributionStatisticConfig.builder().percentiles(0.5, 0.75, 0.9).sla(1).expiry(Duration.ofMinutes(1)).minimumExpectedValue(1L).build().merge(config);}return config;}});Metrics.addRegistry(registry);}