关于dubbox部分rest接口超时问题研究

业务高峰期部分rest接口超时有一段时间了,之前一直怀疑是kafka、nginx、log4j、网络等原因并进行优化,一直没有太大改观。我们生产共有四台nginx反向代理网关,运维在某台nginx中通过日志grep看到,高峰期   nginx反向代理到后端某台tomcat,每秒达到100+,4台nginx则为400+,已超过tomcat设置的并发连接数和完全连接队列的大小(200+100=300)。前两天运维说把tomcat线程数调大到600之后(原来是200),超时问题就没有再出现了。

事后考虑原因分析如下:

tomcat配置中有一个参数叫acceptCount,这个参数在tomcat中指server端监听端口的完全连接队列的socket上限(backlog)。这个值在dubbox中是不能设置的,默认100。

另一个参数叫threadCount,这个参数指tomcat工作线程池大小。tomcat线程池每次从队列头部取线程去处理请求,请求完结束后再放到队列尾部,也就是说前后两次请求处理不会用同一个线程。如下:


关于dubbox部分rest接口超时问题研究

换句话说,之前threadCount=200、acceptCount=100时,tomcat能接受的最大并发数就是200,如果并发数>200时,只会有额外的100个并发请求会在完全连接队列中排队,其他并发请求则根本进入不了完全连接队列中,会被服务端拒绝处理(详见注1)。还有三种情况:

1、半连接队列满了:详见注2

2、在完全连接队列中超时:出现这种情况,主要因为接口处理响应慢,导致tomcat工作线程池没有空闲的线程处理完全连接队列中的请求,完全连接队列中的请求在到达超时时间(20s)时,被nginx主动关闭了tcp连接(nginx日志中http ressponse code:504)。有一点要注意,已经进入完全连接队列中的请求,最终会被tomcat处理,只不过此时连接已经断开,nginx不会收到tomcat的响应。

3、接口处理超时:出现这种情况,说明请求已经在tomcat的工作线程中处理了,确实由于接口处理响应慢,而最终被nginx主动关闭了tcp连接。 

结论:

治标的方法是增加tomcat工作线程池(threadCount)的数量,而治本的方法还是要提高接口的响应速度(rt:response time)。试想如果我们的所有接口都能控制在100ms之内,tomcat工作线程池设置为200,单机也能承受峰值2000(200 / 0.1)的并发量请求量(当然mem和cpu不能太差)。

注1:当accept队列满了之后,即使client继续向server发送ACK的包,也会不被响应,此时ListenOverflows+1,同时server通过/proc/sys/net/ipv4/tcp_abort_on_overflow来决定如何返回,0表示直接丢弃该ACK,1表示发送RST通知client;相应的,client则会分别返回read timeout 或者 connection reset by peer

 注2:对于SYN半连接队列的大小是由(/proc/sys/net/ipv4/tcp_max_syn_backlog)这个内核参数控制的,有些内核似乎也受listen的backlog参数影响,取得是两个值的最小值。当这个队列满了,不开启syncookies的时候,Server会丢弃新来的SYN包,而Client端在多次重发SYN包得不到响应而返回(connection time out)错误。但是,当Server端开启了syncookies=1,那么SYN半连接队列就没有逻辑上的最大值了,并且/proc/sys/net/ipv4/tcp_max_syn_backlog设置的值也会被忽略。

 最后再附录一张tcp连接建立 和 半连接队列和完全连接队列的图,很经典!


关于dubbox部分rest接口超时问题研究
 

参考链接:

tcp的半连接与完全连接队列

https://segmentfault.com/a/1190000008224853

tomcat的acceptCount与maxConnections

https://segmentfault.com/a/1190000008064162