Lettuce監控LatencyUtils引發的JVM GC

最近線上其中一個服務經常內存告警,老年代不足,讓運維dump了線上的服務gc日誌,發現long[]佔用內存總量48%左右,經過JPofiler和MAT分析,定位故障原因大致在lettuce的org.LatencyUtils.LatencyStats中:

經過jdk自帶的VisualVM分析dump文件,lettuce在調用get方法時生成大量對象

在get方法中斷點到lettuce的org.LatencyUtils.LatencyStats#LatencyStats(long, long, int, int, long, org.LatencyUtils.PauseDetector)方法中,count中3456個對象;
由參數highestTrackableLatency指定:


繼續斷點到io.lettuce.core.protocol.CommandHandler#recordLatency


發現是this.clientResources.commandLatencyCollector().isEnabled()這個配置項默認開啓了監控服務,
在lettuce官網中https://lettuce.io/core/5.3.7.RELEASE/reference/index.html

翻譯內容:

The client can collect latency metrics during while dispatching commands. 
Command latency metrics is collected on connection or server level. 
Command latency collection is enabled by default and can be disabled 
by setting commandLatencyCollectorOptions(…) 
to DefaultCommandLatencyCollectorOptions.disabled().

客戶端可以在調度命令期間收集延遲度量。命令延遲度量是在連接或服務器級別收集的。默認情況下,命令延遲收集是啓用的,
可以通過將commandLatencyCollectorOptions(…)設置爲DefaultCommandLatencyCCollectorOptions.disabled()來禁用
於是網上各種搜索關閉LatencyUtils監考的配置項:最後在:https://zhuanlan.zhihu.com/p/398900980搜到
import io.lettuce.core.event.DefaultEventPublisherOptions;
import io.lettuce.core.metrics.DefaultCommandLatencyCollectorOptions;
import io.lettuce.core.resource.DefaultClientResources;
import java.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author:huzhiyang
 * @Date:2023/2/24 15:49
 * @Desc:
 */
@Configuration(proxyBeanMethods = false)
public class LettuceConfiguration {

    /**
     * 每5s 採集一次命令統計
     *
     * @return
     */
    @Bean
    public DefaultClientResources getDefaultClientResources() {
        DefaultClientResources build = DefaultClientResources.builder()
                //關閉監控
                .commandLatencyCollectorOptions(DefaultCommandLatencyCollectorOptions.disabled())
                .commandLatencyPublisherOptions(
                        //每 5s 採集一次命令統計
                        DefaultEventPublisherOptions.builder().eventEmitInterval(Duration.ofSeconds(5)).build())
                .build();
        return build;
    }

}

啓動發現不好使,還是會進入org.LatencyUtils.LatencyStats#LatencyStats方法中,
原因是我們項目在config中配置了LettuceConnectionFactory,於是在LettuceConnectionFactory中進行配置:

private LettuceConnectionFactory getLettuceConnectionFactory(RedisConfiguration poolConfig) {
        //連接池配置
        GenericObjectPoolConfig genericObjectPoolConfig =
                new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(redisProperties.getMaxIdle());
        genericObjectPoolConfig.setMinIdle(redisProperties.getMinIdle());
        genericObjectPoolConfig.setMaxTotal(redisProperties.getMaxActive());
        genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getMaxWait());

        //redis客戶端配置
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
                builder = LettucePoolingClientConfiguration.builder().
                commandTimeout(redisProperties.getTimeout());

        ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                .enablePeriodicRefresh(Duration.ofSeconds(60))// 開啓週期刷新(默認60秒)
                .build();
        ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
                .topologyRefreshOptions(clusterTopologyRefreshOptions)//拓撲刷新
                .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .autoReconnect(true)
                .socketOptions(SocketOptions.builder().keepAlive(true).build())
                .build();

        builder.shutdownTimeout(redisProperties.getTimeout());
        builder.poolConfig(genericObjectPoolConfig);
        builder.readFrom(ReadFrom.SLAVE_PREFERRED);
        builder.clientOptions(clusterClientOptions);
        //關閉監控配置
        builder.clientResources(DefaultClientResources.builder()
                .commandLatencyCollectorOptions(DefaultCommandLatencyCollectorOptions.disabled()).build());
        LettuceClientConfiguration lettuceClientConfiguration = builder.build();

        //根據配置和客戶端配置創建連接
        LettuceConnectionFactory lettuceConnectionFactory = new
                LettuceConnectionFactory(poolConfig, lettuceClientConfiguration);
        lettuceConnectionFactory.setDatabase(redisProperties.getDatabase());
        lettuceConnectionFactory.afterPropertiesSet();
        return lettuceConnectionFactory;
    }

再次啓動,發現服務好使了,通過VisualVM實時健康對比開啓和關閉之後的dump文件;

關閉監控前:

關閉監控後:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章