web服务器使用的是nginx + uwsgi 的架构;近期发现服务器上偶然会出现特殊日志;并且很难重现,有时候出现一段时间会自己回归正常。
问题背景:
web服务器使用的是nginx + uwsgi 的架构;
近期发现服务器上偶然会出现
*** uWSGI listen queue of socket 4 full !!! (100/100) ***
这样的日志;并且很难重现,有时候出现一段时间会自己回归正常,如下图:
图1 uwsgi listen queue full
问题分析:
由于是线上不定时重现,并且自动会好,并且表面上看应该是listen 队列满的原因。排除是代码逻辑等问题,怀疑是 资源和访问方式问题导致的;
首先要在环境中复现这种情况,以便发现更深入的原因;
看到listen 队列自然会找到对应的系统调用 int listen(int fd, int backlog);
socket listen 的时候可以指定 backlog 详细请看下边的定义:
An incomplete connection queue, which contains an entry for each SYN that has arrived from a client for which the server is awaiting completion of the TCP three-way handshake. These sockets are in the SYN_RCVD state .A completed connection queue, which contains an entry for each client with whom the TCP three-way handshake has completed. These sockets are in the ESTABLISHED state.The backlog argument to the listen function has historically specified the maximum value for the sum of both queues.
简单的说backlog 就是 已完成队列和等待完成队列和,所以如果程序压力大,不能及时的accept socket ,那么队列就可能会满,满了以后新来的connection 就建立不起来,这里用c 简单做了个验证,服务器端listen 后不进行accept ,backlog 设置为1 ,让客户端请求时,代码如下:
图2 服务器只监听8000 但是不进行accept动作
图3 并发访问8000端口 TCP状态的变化统计
我们观察到新来的connection 建立不起来,丢弃了很多SYN包;
复现过程:
(1)根据上边的分析,问题应该是请求压力大造成的,所以使用了ab工具来并发构造请求;先随便构造服务器上的一个请求接口来测试;
ab -n 1000 -c 300 http://xxxxxx/xxxxxx/xxxxx/api_test1/
表示请求1000次,并发量是300,查看服务器uwsgi 的log 并无发现异常,加大并发量到500后还是正常;
(2)看来正常情况下虽然是并发很大,也不会出现这种情况,那么就在某一个api里边人工让它处理慢一点,也就是让accept 的速度降下来:
import time
print "-------------", time.time()
time.sleep(10)
(3)再次使用ab 来测试。果然出现了 listen queue of socket 4 full ;
此时在看TCP的状态的统计情况,发现果然有大量的SYN被丢弃掉 ,问题基本上就定位了;
仔细来看,该问题主要原因是由于部分api 处理慢,影响了整体组件其他api的可用性。在并发较高的情况下就会出现该问题;直接后果就是其他api不能正常提供服务;
解决办法:
这里根本原因还需要分析那个慢的api 是否正常,是否可以优化,如果确实是逻辑复杂,很难优化,加上业务上确实处理不过来,那么可以使用下边的方案来解决该问题;
(1)增加uwsgi listen 队列长度 :通过参数 –listen 1024 来提高监听长度;
(2)使用UNIX Domain Socket 来替代网络Socket ,它不需要经过网络协议栈,不需要打包拆包等操作,它只是将应用程序数据从一个进程cp到另一个进程,这正好适合nginx 和 uwsgi 在同一台机器的情况;通过 –socket /tmp/uwsgi.sock 来使用 Domain Socket;
当然两个方法只能暂缓,不能根除;
(3)增加负载均衡,把压力均分到其他机器上;
如果您需要了解更多内容,可以
加入QQ群:570982169
直接询问:010-68438880