[alibaba/nacos]Nacos1.4.2版本,三台集群出现异常

2024-07-18 984 views
8

我们目前发现运行过程中,突然三台节点出现了

2021-09-13 12:08:08,779 ERROR failed to req API:http://10.12.105.24:8848/nacos/v1/ns/distro/checksum

java.net.SocketTimeoutException: 10,000 milliseconds timeout on connection http-outgoing-33476 [ACTIVE] at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.timeout(HttpAsyncRequestExecutor.java:387) at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:92) at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:39) at org.apache.http.impl.nio.reactor.AbstractIODispatch.timeout(AbstractIODispatch.java:175) at org.apache.http.impl.nio.reactor.BaseIOReactor.sessionTimedOut(BaseIOReactor.java:261) at org.apache.http.impl.nio.reactor.AbstractIOReactor.timeoutCheck(AbstractIOReactor.java:502) at org.apache.http.impl.nio.reactor.BaseIOReactor.validate(BaseIOReactor.java:211) at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280) at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104) at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591) at java.lang.Thread.run(Thread.java:748) 2021-09-13 12:08:08,828 ERROR failed to req API:http://10.12.105.26:8848/nacos/v1/ns/distro/checksum

java.net.SocketTimeoutException: 10,000 milliseconds timeout on connection http-outgoing-33477 [ACTIVE] at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.timeout(HttpAsyncRequestExecutor.java:387) at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:92) at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:39) at org.apache.http.impl.nio.reactor.AbstractIODispatch.timeout(AbstractIODispatch.java:175) at org.apache.http.impl.nio.reactor.BaseIOReactor.sessionTimedOut(BaseIOReactor.java:261) at org.apache.http.impl.nio.reactor.AbstractIOReactor.timeoutCheck(AbstractIOReactor.java:502) at org.apache.http.impl.nio.reactor.BaseIOReactor.validate(BaseIOReactor.java:211) at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280) at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104) at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)

这个异常,我们初步认为在选举过程中请求流量一样会打到所有的节点上,导致选主失败,于是我们外层添加了ng检查,一旦切到某一台,然后另外两台就可以迅速恢复,但是只能临时解决下,但是找不到为什么三台突然出现这个问题,防火墙都关闭,服务数量在6000+ ;感觉是健康检查的问题,帮看下

回答

0

我们今天又遇到了这个情况,naming-distro日志里一直在刷上面我发的日志,无法访问到其他两台,因为既然都已经发送请求了,但是其他两台没有给response,同时我们看了三台节点的cpu正常,内存gc频繁(平均3s一次gc,估摸是在发心跳给其他两台节点做健康检查),老年代回收正常,网络无丢包,服务器三台连接都正常,time_waiting也不高;我们在考虑是不是因为内置的tomcat线程池导致的?我看server端的properties里面没有对tomcat线程池做额外的处理,默认内置应该是200,对于我们的服务体量8000+,估计是不够的吧?这个是我提出的猜测,因为请求三台都无法接收到彼此,所有资源又正常,考虑到是内部某个地方做了限制?

7

我们discovery的版本是GreenWish版本,官方选型的版本 image

0

看看服务器的连接数是不是太多导致了不能再建立连接,nacos1.4.2的内部通信是建立的短连接,当请求结束后连接状态会变成TIME_WAIT,只要有nacos-client端发来心跳都会向其他节点同步数据,这个同步数据的请求就是建立的短连接。 linux默认TIME_WAIT的连接会等1分钟才关闭,如果是因为连接过多,试试把net.ipv4.tcp_tw_recycle改为1再看看效果

4

这个参数在很早的时候调整过,那是开发人员本地注册上来,后面我们linux设置了这个参数,并且禁止了本地开发注册服务到nacos,昨晚抓了线程日志,发现线程日志有200个线程,这200个线程是tomcat的线程池的线程,而在nacos源码中有涉及到httpclient连接池的构建,线程日志如下,我标红的地方,表示卡了很多线程都在获取连接上 image 而nacos源码中,这段代码,默认给的连接个数是虚拟机处理器*2,我们是8c,也就是连接池只有16个连接,并且这些连接都没有做默认超时释放连接的操作,初步断定是这块的原因:并没有设置connectionTimeout时间。 线程日志中tomcat200个线程,都被消耗在获取连接的上面,集群在进行服务检查和同步syncData的过程中会不断的拿连接,而连接获取不到,三台的200线程都被消耗完,彼此死锁无法处理其他的请求。

所以我们当把流量打到1台上后,靠linux的socket机制自动会销毁这些连接,释放线程,才会在短时间内恢复正常!! 这块请官方详细看下是不是有遗漏?

2
d65570e317bdc30fe39229ecb688d6c

补充下nacos源码图片没上传上去

9

这个不能随便设置,会导致网络异常,你看下我后面发的分析的消息

1

@DoctChen 您好是否可以提供一个demo呢? 听您所述 用的是nacos的AP模式 https://www.processon.com/view/link/6037a1131e085364c673e303

taskDispatcher.addTask(key)方法 在同步实例信息给其他节点的时候,需要从线程池拿链接 我理解是您认为这里需要给HttpClient设置一个超时时间对么

1

是这个意思,而且我测试过ap模式下HttpClientConfig构建一个连接的时候,如果没有主动释放连接,这个连接会超时,而最终由服务器四次握手关闭,关闭后的连接处于Timewaiting状态等待被释放,所以一直有服务注册上来,节点同步是一直在进行的,而且选举,同步节点数据给其他服务的时候,这个时候在开发阶段,会有很多的连接打上来,连接不释放在timewaiting立刻就被用完,后面所有的连接都无法响应,后续就卡死。

5

节点之间同步实例checksum的syncData是http异步方式,HttpClientManager从连接池获取连接时除了ProcessorHttpClientFactory之外都没有调用setConnectionRequestTimeout设置获取连接超时。nacos集群中的某个节点出问题时,节点之间会爆发很多http连接,这些请求可能会逐个搞垮剩余的节点。 此外1.x分支的createNacosAsyncRestTemplate也没有像2.0分支那样增加IOReactor异常Handler。 自己修改AbstractHttpClientFactory和DefaultHttpClientRequest可以参考 https://github.com/alibaba/nacos/pull/7376/files

这里面有一个坑,HttpClientManager创建各个xxxClientFactory时设置的各个requestConfig参数存入了构建的InternalHttpAsyncClient或InternalHttpClient对象的defaultConfig属性。如果http请求创建的请求Entity包含requestConfig,那么defaultConfig就会失效,见InternalHttpClient的doExecute方法(同步httpclient-4.5.jar)、 MainClientExec的prepare方法(异步httpasyncclient-4.1.3.jar):

            if (config != null) {
                localcontext.setRequestConfig(config);
            }

本来想在DefaultHttpClientRequest的replaceDefaultConfig方法里先将client的defaultConfig作为HttpRequestBase的config的初始参数与http请求配置的requestConfig进行合并,但是InternalHttpClient和InternalHttpAsyncClient这两个类没有任何修饰,反射才能取到defaultConfig成员,代码很难看。 最后还是给DefaultAsyncHttpClientRequest和DefaultHttpClientRequest的构造方法加了一个defaultConfig入参,把原来的replaceDefaultConfig方法改成了mergeDefaultConfig。

2
我们是8c,也就是连接池只有16个连接,并且这些连接都没有做默认超时释放连接的操作,初步断定是这块的原因:并没有设置connectionTimeout时间。

可能并不是建立连接的超时,而是setConnectionRequestTimeout,从连接池获取连接要设置超时,否则默认是等待LONG.MAX_VALUE,如果不设置ConnectionRequestTimeout,我们极限压测可以稳定重现OOM: #7375 后来设成5秒无论怎么压,内存都会稳定在一个平衡点。

受你这个帖子启发,我们把HttpClientManager的ApacheSyncHttpClientFactory的buildHttpClientConfig方法改了:

  1. setMaxConnPerRoute改成200(参考前面AsyncHttpClientFactory写的是128,用于节点间同步持久实例的异步连接数),设置200是我们没有改nacos节点内置的tomcat的maxThreads参数,这个参数默认是200,连接请求超过目标节点的工作线程数也需要等待,还不如保持一致。
  2. setMaxConnTotal参照节点数-1乘上setMaxConnPerRoute设置了,考虑到动态扩容的场景,可以设置的大一些。
  3. setConnectionTimeToLive这个参数我们直接改成-1了,我们节点之间的心跳转发请求很频繁,设置连接存活参数没有意义。

通过上述修改,我们压测16c32g配置的5节点能够稳定支撑30万临时实例(带鉴权)。

写AsyncHttpClientFactory和ApacheSyncHttpClientFactory可能是两个人写的,基于cpu核数设置线程池大小或许还可以,设置节点之间的高频心跳转发连接池大小就有点离谱了。