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:

官网地址:spring-boot:metrics

一个是禁用Metrics(Actuator)的统计在内存中,

management.metrics.export.simple.enabled=false

还有一个是使用别的registry。

猜你喜欢

转载自blog.csdn.net/lz710117239/article/details/81674209