SpringBoot原始碼解析(八)Actuator記憶體溢位
Springboot中,我們可以使用監控工具Actuator,檢視和變更spring的狀態,但是Actuator是有可能引起記憶體溢位的問題的,具體原因,分析如下:
一、Filter
在Actuator中,有一個過濾器,即MetricsWebFilter,請求監控過濾器,其filter方法如下:
private Publisher<Void> filter(ServerWebExchange exchange, Mono<Void> call) { long start = System.nanoTime(); return call.doOnSuccess((done) -> success(exchange, start)) .doOnError((cause) -> error(exchange, start, cause)); }
當請求完成後,無論是成功或者是失敗,其都會做相應的處理,成功:
private void success(ServerWebExchange exchange, long start) {
Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, null);
this.registry.timer(this.metricName, tags).record(System.nanoTime() - start,
TimeUnit.NANOSECONDS);
}
失敗:
private void error(ServerWebExchange exchange, long start, Throwable cause) { Iterable<Tag> tags = this.tagsProvider.httpRequestTags(exchange, cause); this.registry.timer(this.metricName, tags).record(System.nanoTime() - start, TimeUnit.NANOSECONDS); }
在success和error方法的第一行,建立了一個tags,tags是用method,uri,status等組成的一個集合。
2、Meter.Id
後面再Timer類的register方法中,tags成了Meter.Id中的一個屬性,被後續使用:
public Timer register(MeterRegistry registry) { // the base unit for a timer will be determined by the monitoring system implementation return registry.timer(new Meter.Id(name, tags, null, description, Type.TIMER), distributionConfigBuilder.build(), pauseDetector == null ? registry.config().pauseDetector() : pauseDetector); }
在後面獲取Meter的時候,會檢視是否已經在記憶體中快取了Meter,如果存在就返回相應的Meter,如果不存在就建立Meter
private Meter getOrCreateMeter(@Nullable DistributionStatisticConfig config,
BiFunction<Id, /*Nullable Generic*/ DistributionStatisticConfig, Meter> builder,
Id mappedId, Function<Meter.Id, ? extends Meter> noopBuilder) {
Meter m = meterMap.get(mappedId);
if (m == null) {
if (isClosed()) {
return noopBuilder.apply(mappedId);
}
synchronized (meterMapLock) {
m = meterMap.get(mappedId);
if (m == null) {
m = builder.apply(mappedId, config);
此時我們看下Meter.Id的equals方法,看下是怎麼判斷資料是否存在的,如下
return Objects.equals(name, meterId.name) && Objects.equals(tags, meterId.tags);
可以看到equals方法中,判斷了name和tags都相等,才算存在。
三、記憶體溢位
在LatencyStats類中,有許多的物件,佔用了部分記憶體空間:
private volatile AtomicHistogram activeRecordingHistogram;
private Histogram activePauseCorrectionsHistogram;
private AtomicHistogram inactiveRawDataHistogram;
private Histogram inactivePauseCorrectionsHistogram;
因為每次引數變動都會生成一個LatencyStats,由於這些LatencyStats一直存在於記憶體中,請求次數多了,便會造成記憶體溢位。
四、解決辦法
解決方法有兩個,其官網介紹如下:
Micrometer ships with a simple, in-memory backend that is automatically used as a fallback if no other registry is configured. This allows you to see what metrics are collected in the metrics endpoint.
The in-memory backend disables itself as soon as you’re using any of the other available backend. You can also disable it explicitly:
一個是禁用Metrics(Actuator)的統計在記憶體中,
management.metrics.export.simple.enabled=false
還有一個是使用別的registry。