使用uwsgi + nginx时出现"资源暂时不可用"的问题

21
我使用nginx-uWSGI来托管我的Django应用程序。这是我的uWSGI配置:

I've django app host using nignx-uwsgi. Here is my uwsgi configuration:


[uwsgi]
 master          = true 
 socket          = /var/uwsgi/uwsgi.sock
 chmod-socket    = 666
 chdir           = /home/ubuntu/test
 wsgi-file       = /home/ubuntu/test/test/wsgi.py
 virtualenv      = /home/ubuntu/virtual
 vacuum          = true
 enable-threads  = true
 daemonize= /home/ubuntu/uwsgi.log

我在nginx日志中看到了错误信息:

2017/06/16 04:25:42 [error] 26129#0: *1141328 connect() to unix:///var/uwsgi/uwsgi.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: xxx.xxx.xx, server:

同时网站显示502坏的网关。我必须重新启动uwsgi才能解决问题,但是错误发生的频率越来越高了。是否有什么方法可以解决这个问题?

2个回答

22

当服务器负载较重时,会出现此错误。我先尝试增加worker_connections的值,但没有奏效。uWSGI的默认队列大小为100,因此当从Nginx传递给uWSGI的请求超过100个时,队列就会满,Nginx会向客户端抛出502错误。要解决这个问题,需要增加uWSGI的队列大小。在uwsgi.ini文件中添加“listen={所需队列大小}”。在我的情况下,我写了:listen=200。

但在执行此操作之前,必须检查$ cat /proc/sys/net/core/somaxconn,默认值为128,因此需要增加其值: $echo 200 > /proc/sys/net/core/somaxconn
或 $ sysctl -w net.core.somaxconn=200


2
很棒的回答!文档链接如下:https://uwsgi-docs.readthedocs.io/en/latest/articles/TheArtOfGracefulReloading.html#the-listen-queue请注意,现代Ubuntu内核(20.04)似乎具有默认的最大连接数4096。 - Richard

1
我同意@Vicky Gupta的回答,但我想展示一下你如何看到正在发生的事情。在uwsgi/nginx运行的shell中,
watch -n0.3 ss -xl
ss命令显示实时套接字使用情况。 -x表示UNIX套接字,-l表示处于LISTEN状态的套接字。在本讨论中,我们不关心uwsgi打开的所有随机未命名套接字(这些套接字可能处于ESTABLISHED状态)。只关注一个叫做uwsgi.sock的套接字。应该看到类似以下的结果。(抱歉,格式混乱了。)

output of ss command for one socket in listening state

参见,例如https://www.linux.com/topic/networking/introduction-ss-command/(或任何可靠的资源),以了解有关ss命令输出的更多信息。这里的关键指标是值,它应该显示在Recv-Q下面。通常情况下,除非您观察到结果快速变化(例如0.3秒),或者您的应用程序负载很重,否则此值将保持为0。
(由于某种原因,在我的uwsgi / nginx设置中,uwsgi似乎总是将Send-Q保持在最大值。我不确定是我做错了什么,还是他们做错了什么,或者是其他人。但是,在这里并不重要。)
在观察Recv-Q的值时运行负载测试。如果它达到100然后停止,即使您继续增加工作进程/负载,同时在nginx日志中出现"resource temporarily unavailable",那么这非常强有力地证明您已经达到了限制。
一般来说,并发请求的数量似乎与接收队列中等待处理的项目数量相匹配。对我来说,我有两个处理请求的API实例,当我将每秒请求数量扩展到大约200+时,我几乎可以精确地复现资源暂时不可用的问题,而我的监听队列长度为100。
顺便说一下,这是我为了对我的应用进行负载测试而编写的一个Python脚本,如果你需要的话可以用一下。 python load-test.py 150
# load-test.py

import sys
import os
import time
import requests
import functools
import concurrent.futures
import multiprocessing


def make_request(id_):
    while True:
        try:
            print(f"Thread {id_}: requesting")
            resp = requests.get(f"{os.environ['API_URL']}/my/test/api/", headers=dict(Authorization=os.environ["AUTH_HEADER"]))
        except Exception as e:
            print(f"Thread {id_}: error: {e}")
        else:
            print(f"Thread {id_}: resp: {resp}")


def manager(num_workers=None):
    print(f"Started new main process: {os.getpid()}")
    kwargs = dict(max_workers=num_workers) if num_workers else dict()
    with concurrent.futures.ThreadPoolExecutor(**kwargs) as executor:
        executor.map(make_request, range(executor._max_workers))


if __name__ == "__main__":

    if len(sys.argv) > 1:
        num_workers = int(sys.argv[1])
    else:
        num_workers = os.cpu_count() or 1
        print(f"Choosing {num_workers} workers, the same as detected CPU count")

    _manager = multiprocessing.Process(
        target=functools.partial(manager, num_workers=num_workers)
    )
    try:
        _manager.start()
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        _manager.terminate()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接