性能测试发现“redis cannot assign requested address”问题

场景描述:test_client(jmeter) -> nginx -> redis

问题表现:nginx频繁报错redis cannot assign requested address,查看nginx网络连接情况,发现有大量TIME_WAIT的连接,并且test_client的tps上不去。大量的TIME_WAIT状态使得local port在TIME_WAIT持续期间不能被再次分配,即没有可用的local port,最终导致新建连接失败。

问题分析一:

大量TIME_WAIT出现的主要原因是nginx频繁与服务端建立tcp短连接后,又主动关闭tcp连接引起的。我们使用的openresty的redis库(resty.redis)已经有连接池的概念,且关闭tcp连接时使用set_keepalive方式,所以搞不清楚为什么nginx会频繁关闭tcp连接,导致出现大量TIME_WAIT的tcp连接。

问题分析二:

lua-resty-redis的set_keepalive函数最终调用的是lua-nginx-module的tcpsock:setkeepalive函数,此函数有两个参数(max_idle_timeout, pool_size),即最大空闲时间和最大连接池大小。使用set_keepalive把该连接放到连接池后,该连接到达最大空闲时间或者连接池到达连接池最大值时,会把连接池中最久未使用的连接关闭掉。原文如下:

When the connection pool exceeds the available size limit, the least recently used (idle) connection already in the pool will be closed to make room for the current connection.

问题分析三:

基于以上的分析,当nginx的worker pool设置成128时(worker_processes=1),如果有200个并发连接(循环请求1000次)打到nginx,nginx会同时建立200个连接请求后端redis服务器,当200个并发连接第一次请求结束后,调用set_keepalive把连接归还给nginx(准确地说应该是lua-nginx-module)的连接池时,大约会有72(200-128)个连接无法放回连接池而关闭(tcpsock:close)。然后200个并发连接发起第二次请求,能够从连接池取到的可用连接中只有128个,剩余的72个连接只能再次建立tcp连接到后端redis服务器,请求结束后200个新连接放回到连接池的时候,由于连接池大小的限制只能有部分(或者一个都没有)连接放回连接池,大部分tcp连接还是只能关闭。这样周而复始,TIME_WAIT的连接会越来越多,且没有可用的local port,最终导致新建连接失败。

问题解决方式一:

这个问题可以通过设置两个内核参数(tcp_tw_recycle和tcp_tw_reuse)来间接解决,但tcp_tw_recycle有副作用,不建议打开。

但是这种方法治表不治本,即使可以快速回收/复用TIME_OUT的tcp连接,但是其实不能复用连接池连接时,还是需要新建nginx到redis服务器的tcp连接(因为已经close的tcp连接到达TIME_WAIT状态时,reuse此连接还是需要重新建立到redis server的tcp连接),所以开销还是很大。

问题解决方式二:

我们的解决方法是根据现实的需要,动态调整nginx到redis连接池的大小。如果我们最大支持test_client的1000个并发连接,那我们nginx到redis的连接池设置成1024(当然redis server的maxclient也要大于1024,否则瓶颈就变成redis server了)。这种解决方式并不完美,因为这样可能导致nginx空闲时有大量到redis server的tcp连接。后期可以参考ThreadPoolExecutor设置corePoolSize,maximumPoolSize或许更加合理(需要修改lua-nginx-module代码)。

PS:上面说到nginx到redis的连接成设置成1024(此值针对单nginx worker),对于worker_processes>1时也需要这样设置,是考虑到可能nginx master到nginx worker分配极端不均匀的情况。其实nginx性能还好,如果worket_processes=4时,此值设置成512绰绰有余。

参考链接:

https://github.com/openresty/lua-resty-redis

https://github.com/openresty/lua-nginx-module

https://github.com/openresty/lua-nginx-module#tcpsocksetkeepalive

http://blog.csdn.net/yunhua_lee/article/details/8146856

http://www.cnblogs.com/javawebsoa/archive/2013/05/18/3086034.html

http://jinnianshilongnian.iteye.com/blog/2187328

 
 

猜你喜欢

转载自murray2081.iteye.com/blog/2283913