[alibaba/nacos]升级2.0.4内存泄漏

2025-10-31 742 views
5

目前我们打算将nacos server从1.4.4升级到2.0.4,跑了两天发现内存泄漏和full gc频繁 前置条件:

nacos集群为三节点

每节点配置:8C8G,堆为4g

client端版本:1.4.0

cliient连接server三节点方式为直接填写三节点ip,中间没有经过vip

从1.4.4升级到2.0.4过程中正常

首先是发现其中一个节点cpu明显增高

分析gc日志,可以看到full gc过于频繁,且gc前后堆大小几乎没变化

MAT分析堆栈 这里的byte数组经过排查,保存的是/nacos/v1/cs/configs/listener接口的信息,每一个占520KB,总共2267个,1.2g左右 去掉软,弱引用查看gc root,被两个http线程引用(17,23) 总共有203个http-nio-8848-*线程,但比较奇怪的是,我任意找的每个/nacos/v1/cs/configs/listener都是被这两个http线程引用。 java.nio.HeapByteBuffer有36000+个,其中部分也是被这两个线程引用。 看了下别的http线程,基本都是Time-WAIT状态 没搞明白为什么这个节点会有那么多的/nacos/v1/cs/configs/listener hold在这里,分析了另一个没有内存泄露的节点,hold住的/nacos/v1/cs/configs/listener只有134个

几点疑惑: 1、在开发,测试,预发环境升级都不约而同的出现某个节点会内存泄露的问题。 2、给我的感觉是似乎客户端的请求都发到了这一个节点?而不是另外的节点? 3、其余没有发现什么问题,接下来如何排查呢?

回答

1

同时客户端会有如下异常出现:

java.net.SocketTimeoutException: connect timed out
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:607)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
    at sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
    at sun.net.www.http.HttpClient.New(HttpClient.java:339)
    at sun.net.www.http.HttpClient.New(HttpClient.java:357)
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1226)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1162)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1056)
    at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:990)
    at com.alibaba.nacos.common.http.client.request.JdkHttpClientRequest.execute(JdkHttpClientRequest.java:109)
    at com.alibaba.nacos.common.http.client.NacosRestTemplate.execute(NacosRestTemplate.java:482)
    at com.alibaba.nacos.common.http.client.NacosRestTemplate.exchangeForm(NacosRestTemplate.java:427)
    at com.alibaba.nacos.client.naming.net.NamingProxy.callServer(NamingProxy.java:600)
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:525)
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:492)
    at com.alibaba.nacos.client.naming.net.NamingProxy.sendBeat(NamingProxy.java:427)
    at com.alibaba.nacos.client.naming.beat.BeatReactor$BeatTask.run(BeatReactor.java:167)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
ErrCode:500, ErrMsg:org.apache.http.conn.ConnectTimeoutException: Connect to xxx:8848 [/xxx] failed: Connect timed out
    at com.alibaba.nacos.client.naming.net.NamingProxy.callServer(NamingProxy.java:612)
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:525)
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:492)
    at com.alibaba.nacos.client.naming.net.NamingProxy.sendBeat(NamingProxy.java:427)
    at com.alibaba.nacos.client.naming.beat.BeatReactor$BeatTask.run(BeatReactor.java:167)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
6

listener应该是长轮训, 你看下config-memory.log有多少client count

5

listener应该是长轮训, 你看下config-memory.log有多少client count

感谢 @KomachiSion 回复,我看了下三节点分别的client count:

出现内存泄露的这一台:

其余两台: 感觉差的有点多。。。我客户端连接的方式是直接填了三节点的ip,没有使用vip?这是客户端轮训方式的问题?

8

arthas跟了一下代码,我发现获取server的方法:com.alibaba.nacos.client.config.impl.ServerListManager#getCurrentServerAddr,这里永远获取的是一个固定的值。 这里的currentServerAddr只有当服务获取失败时才会变更,那么这样的话,确实有可能在滚动更新的时候,只有一个server可用,然后全部去那个server节点了,看来还是得加一层vip在前面。

    public String getCurrentServerAddr() {
        if (StringUtils.isBlank(currentServerAddr)) {
            iterator = iterator();
            currentServerAddr = iterator.next();
        }
        return currentServerAddr;
    }

@KomachiSion 是不是需要加一层VIP在前面呢?

7

nacos-config client的轮询,在某个ip可以访问的时候会一直使用该ip访问, 看起来是你的压力不均匀,所有的请求都打到一台机器导致压力过大。

可以考虑加一层vip,使用最小连接负载均衡方式。

1

或者考虑客户端逐渐升级到2.0以上, 使用长连接后,监听对内存的损耗会小很多。

9

谢谢