共计 6910 个字符,预计需要花费 18 分钟才能阅读完成。
原文链接
Metrics 是一个提供服务性能检测工具的 Java 类库,它提供了功能强大的性能指标工具库用于度量生产环境中的各要害组件性能。
度量类型
Metrics 提供了以下几种根本的度量类型:
Gauge
:用于提供自定义度量。Counter
:计数器,实质是一个java.util.concurrent.atomic.LongAdder
。Histogram
:直方图数据。Meter
:统计零碎中某一事件的响应速率,如 TPS、QPS。该项指标值间接反馈零碎以后的解决能力Timer
:计时器,是Meter
和Histogram
的联合,能够统计接口申请速率和响应时长。
Gauge
Gauge
是对一项值的刹时度量。咱们能够通过实现 Gauge
接口来依据业务场景自定义度量。
例如,想要度量队列中处于期待状态的作业数量:
public class QueueManager {
private final Queue queue;
public QueueManager(MetricRegistry metrics, String name) {this.queue = new Queue();
// 通过 MetricRegistry 的 register 办法注册 Gauge 度量
metrics.register(MetricRegistry.name(QueueManager.class, name, "size"),
new Gauge<Integer>() {
@Override
public Integer getValue() {return queue.size();
}
});
}
}
官网目前提供了以下几种 Gauge
实现:
Counter
Counter
是一个惯例计数器,用于对某项指标值进行累加或者递加操作。
Counter
实质是一个 java.util.concurrent.atomic.LongAdder
,在多线程同时更新计数器的场景下,当并发量较大时,LongAdder
比AtomicLong
具备更高的吞吐量,当然空间资源耗费也更大一些。
final Counter evictions = registry.counter(name(SessionStore.class, "cache-evictions"));
evictions.inc();
evictions.inc(3);
evictions.dec();
evictions.dec(2);
Histograms
Histogram
反馈的是数据流中的值的散布状况。蕴含最小值、最大值、平均值、中位数、p75、p90、p95、p98、p99 以及 p999 数据分布状况。
private final Histogram responseSizes = metrics.histogram(name(RequestHandler.class, "response-sizes"));
public void handleRequest(Request request, Response response) {
// etc
responseSizes.update(response.getContent().length);
}
Histogram
计算分位数的办法是先对整个数据集进行排序,而后取排序后的数据集中特定地位的值(比方 p99 就是取倒序 1% 地位的值)。这种形式适宜于小数据集或者批处理零碎,不适用于要求高吞吐量、低延时的服务。
对于数据量较大,系统对吞吐量、时延要求较大的场景,咱们能够采纳抽样的形式获取数据。通过动静地抽取程序运行过程中的可能代表零碎实在运行状况的一小部分数据来实现对整个零碎运行指标的近似度量,这种办法叫做蓄水池算法(reservoir sampling)。
Metrics 中提供了各式各样的 Reservoir
实现:
Meter
Meter
用于度量事件响应的均匀速率,它示意的是应用程序整个运行生命周期内的总速率(总申请响应量 / 解决申请的总毫秒数,即每秒申请数)。
除此之外,Meter
还提供了 1 分钟、5 分钟以及 15 分钟的动静均匀响应速率。
final Meter getRequests = registry.meter(name(WebProxy.class, "get-requests", "requests"));
getRequests.mark();
getRequests.mark(requests.size());
Timer
Timer
会度量服务的响应速率,同时也会统计服务响应时长的散布状况。
final Timer timer = registry.timer(name(WebProxy.class, "get-requests"));
final Timer.Context context = timer.time();
try {// handle request} finally {context.stop();
}
Reporters
通过上述各项度量监测服务指标后,咱们能够通过 Reporters 报表导出度量后果。metrics-core
模块中实现了以下几种导出指标的 Report:
Console Reporters
定时向控制台发送服务的各项指标数据。
final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.MINUTES);
CsvReporter
定时向给定目录下的 .csv
文件追加服务各项指标数据。对于每一项指标都会在指定目录下创立一个 .csv
文件,而后定时(本例中是 1s)向每个文件中追加指标最新数据。
final CsvReporter reporter = CsvReporter.forRegistry(registry)
.formatFor(Locale.US)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build(new File("~/projects/data/"));
reporter.start(1, TimeUnit.SECONDS);
JmxReporter
将服务的各项度量指标通过 JMX MBeans 裸露进去,之后能够应用 VisualVM 查看指标数据。生产环境不倡议应用。
final JmxReporter reporter = JmxReporter.forRegistry(registry).build();
reporter.start();
Slf4jReporter
Slf4jReporter
容许咱们将服务的指标数据作为日志记录到日志文件中。
final Slf4jReporter reporter = Slf4jReporter.forRegistry(registry)
.outputTo(LoggerFactory.getLogger("com.example.metrics"))
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.MINUTES);
如何应用
间接援用
间接依赖 Metrics 的外围库,通过其提供的各类 API 实现服务指标数据度量。
- 引入 Maven 依赖
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>${metrics.version}</version>
</dependency>
- 创立一个
MetricRegistry
对象,它是 Metrics 类库的外围类,所有的利用指标都须要注册到MetricRegistry
。
// 实例化 MetricsRegistry
final MetricRegistry metrics = new MetricRegistry();
// 开启 Console Reporter
startConsoleReporter();
Meter requests = metrics.meter(name(MetricsConfig.class, "requests", "size"));
requests.mark();
void startReport() {ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.SECONDS);
}
整合 Spring
- 引入 Maven 依赖
<dependency>
<groupId>com.ryantenney.metrics</groupId>
<artifactId>metrics-spring</artifactId>
<version>3.1.3</version>
</dependency>
- 通过 Java 注解配置 Metrics。
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Configuration;
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.ryantenney.metrics.spring.config.annotation.EnableMetrics;
import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter;
@Configuration
@EnableMetrics
public class SpringConfiguringClass extends MetricsConfigurerAdapter {
@Override
public void configureReporters(MetricRegistry metricRegistry) {
// registerReporter allows the MetricsConfigurerAdapter to
// shut down the reporter when the Spring context is closed
registerReporter(ConsoleReporter
.forRegistry(metricRegistry)
.build())
.start(1, TimeUnit.MINUTES);
}
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({DelegatingMetricsConfiguration.class})
public @interface EnableMetrics {
// 默认 false 示意应用 JDK 动静代理,设置为 true 时示意应用 CGLIB 动静代理(当应用基于类的服务裸露形式时)boolean exposeProxy() default false;
// 设置为 true 时,指标对象能够通过 AopContext.currentProxy()拜访封装它的代理
boolean proxyTargetClass() default false;}
应用限度
因为 Spring AOP 中,只有申明为 public
的办法能够被代理,所以 @Timed
, @Metered
, @ExceptionMetered
以及 @Counted
在 non-public
无奈失效。
因为 @Gauge
注解不波及代理,所以它能够被利用在 non-public
属性和办法上。
public class MetricsBeanPostProcessorFactory {private MetricsBeanPostProcessorFactory() { }
public static AdvisingBeanPostProcessor exceptionMetered(MetricRegistry metricRegistry, ProxyConfig proxyConfig) {return new AdvisingBeanPostProcessor(ExceptionMeteredMethodInterceptor.POINTCUT, ExceptionMeteredMethodInterceptor.adviceFactory(metricRegistry), proxyConfig);
}
public static AdvisingBeanPostProcessor metered(MetricRegistry metricRegistry, ProxyConfig proxyConfig) {return new AdvisingBeanPostProcessor(MeteredMethodInterceptor.POINTCUT, MeteredMethodInterceptor.adviceFactory(metricRegistry), proxyConfig);
}
public static AdvisingBeanPostProcessor timed(MetricRegistry metricRegistry, ProxyConfig proxyConfig) {return new AdvisingBeanPostProcessor(TimedMethodInterceptor.POINTCUT, TimedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig);
}
public static AdvisingBeanPostProcessor counted(MetricRegistry metricRegistry, ProxyConfig proxyConfig) {return new AdvisingBeanPostProcessor(CountedMethodInterceptor.POINTCUT, CountedMethodInterceptor.adviceFactory(metricRegistry), proxyConfig);
}
public static GaugeFieldAnnotationBeanPostProcessor gaugeField(MetricRegistry metricRegistry) {return new GaugeFieldAnnotationBeanPostProcessor(metricRegistry);
}
public static GaugeMethodAnnotationBeanPostProcessor gaugeMethod(MetricRegistry metricRegistry) {return new GaugeMethodAnnotationBeanPostProcessor(metricRegistry);
}
public static CachedGaugeAnnotationBeanPostProcessor cachedGauge(MetricRegistry metricRegistry) {return new CachedGaugeAnnotationBeanPostProcessor(metricRegistry);
}
public static MetricAnnotationBeanPostProcessor metric(MetricRegistry metricRegistry) {return new MetricAnnotationBeanPostProcessor(metricRegistry);
}
public static HealthCheckBeanPostProcessor healthCheck(HealthCheckRegistry healthRegistry) {return new HealthCheckBeanPostProcessor(healthRegistry);
}
}
除此此外,在一个办法中调用处于同一个类中的另一个带有 Metrics 注解的办法时,办法执行流程不会通过代理。