频繁的工作线程超时



我已经设置了具有 3 个工作线程、30 个工作线程连接并使用 eventlet worker 类的 gunicorn。它设置在Nginx后面。每发出几个请求后,我都会在日志中看到这一点。

[ERROR] gunicorn.error: WORKER TIMEOUT (pid:23475)
None
[INFO] gunicorn.error: Booting worker with pid: 23514

为什么会这样?我怎样才能找出出了什么问题?

我们在使用 Django+nginx+gunicorn 时遇到了同样的问题。从 Gunicorn 文档中,我们配置了几乎没有区别的优雅超时。

经过一些测试,我们找到了解决方案,要配置的参数是:超时(而不是优雅超时)。它像时钟一样工作。

所以,做:

1) 打开 Gunicorn 配置文件

2) 将超时设置为您需要的任何值 - 该值以秒为单位

NUM_WORKERS=3
TIMEOUT=120
exec gunicorn ${DJANGO_WSGI_MODULE}:application 
--name $NAME 
--workers $NUM_WORKERS 
--timeout $TIMEOUT 
--log-level=debug 
--bind=127.0.0.1:9000 
--pid=$PIDFILE

在谷歌云上只需将--timeout 90添加到入口点即可app.yaml

entrypoint: gunicorn -b :$PORT main:app --timeout 90

运行 Gunicorn with --log-level debug .

它应该为你提供应用堆栈跟踪。

在 Azure 应用

服务(Linux 应用)上运行 Flask 应用的 Microsoft Azure 官方文档指出超时的使用为 600

gunicorn --bind=0.0.0.0 --timeout 600 application:app

https://learn.microsoft.com/en-us/azure/app-service/configure-language-python#flask-app

此端点是否花费了太多时间?

也许您在没有异步支持的情况下使用 flask,因此每个请求都会阻止调用。若要创建异步支持而不会造成困难,请添加gevent辅助角色。

使用 gevent,新调用将生成一个新线程,并且您的应用程序将能够接收更多请求

pip install gevent
gunicon .... --worker-class gevent

WORKER TIMEOUT表示应用程序无法在定义的时间内响应请求。您可以使用 gunicorn 超时设置进行此设置。某些应用程序比其他应用程序需要更多的时间来响应。

可能影响这一点的另一件事是选择工作线程类型

默认同步工作程序假定应用程序在 CPU 和网络带宽方面受资源限制。通常,这意味着应用程序不应执行任何需要未定义时间的操作。需要未定义时间的一个例子是对互联网的请求。在某些时候,外部网络会以这样的方式发生故障,客户端将堆积在您的服务器上。因此,从这个意义上说,任何向 API 发出传出请求的 Web 应用程序都将受益于异步工作线程。

当我遇到与您相同的问题时(我尝试使用 Docker Swarm 部署我的应用程序),我尝试增加超时并使用其他类型的 worker 类。但都失败了。

然后我突然意识到我的资源限制得太低,无法满足撰写文件中的服务。在我的情况下,这是减慢应用程序速度的事情

deploy:
  replicas: 5
  resources:
    limits:
      cpus: "0.1"
      memory: 50M
  restart_policy:
    condition: on-failure

所以我建议你首先检查一下是什么事情减慢了你的应用程序

会不会是这个?http://docs.gunicorn.org/en/latest/settings.html#timeout

其他可能性可能是您的回复时间太长或等待时间过长。

这对我有用:

gunicorn app:app -b :8080 --timeout 120 --workers=3 --threads=3 --worker-connections=1000

如果您有eventlet添加:

--worker-class=eventlet

如果您有gevent添加:

--worker-class=gevent

我在 Docker 中遇到了同样的问题。

在 Docker 中,我保持训练LightGBM模型 + Flask服务请求。作为HTTP服务器,我使用了gunicorn 19.9.0.当我在 Mac 笔记本电脑上本地运行我的代码时,一切都很完美,但是当我在 Docker 中运行该应用程序时,我的 POST JSON 请求冻结了一段时间,然后gunicorn工作人员因[CRITICAL] WORKER TIMEOUT异常而失败。

尝试了大量不同的方法,但唯一解决我的问题的方法就是添加worker_class=gthread

这是我的完整配置:

import multiprocessing
workers = multiprocessing.cpu_count() * 2 + 1
accesslog = "-" # STDOUT
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(q)s" "%(D)s"'
bind = "0.0.0.0:5000"
keepalive = 120
timeout = 120
worker_class = "gthread"
threads = 3

您需要使用其他工作线程类型类,例如 gevent龙卷风,请参阅以下内容以获取更多说明:第一次外植 :

如果您预计应用程序代码在请求处理期间可能需要长时间暂停,则可能还需要安装 Eventlet 或 Gevent。

第二个:

默认同步工作线程假定应用程序在 CPU 和网络带宽方面受资源限制。通常,这意味着应用程序不应执行任何需要未定义时间的操作。例如,对互联网的请求符合此标准。在某些时候,外部网络会以这样的方式发生故障,客户端将堆积在您的服务器上。

我遇到了非常相似的问题,我也尝试使用"runserver"看看我是否能找到任何东西,但我只有一条消息Killed

所以我认为这可能是资源问题,我继续为实例提供更多 RAM,它奏效了。

如果您使用的是 GCP,则必须为每个实例类型设置辅助角色。

链接到 GCP 最佳实践 https://cloud.google.com/appengine/docs/standard/python3/runtime

超时是此问题的关键参数。

但是它不适合我。

我发现当我设置 workers=1 时没有 Gunicorn 超时错误。

当我查看我的代码时,我在服务器初始化中找到了一些套接字连接(socket.send & socket.recv)。

socket.recv 会阻止我的代码,这就是为什么当 workers>1 时它总是超时的原因

希望给那些对我有问题的人一些想法

对我来说

,解决方案是将--timeout 90添加到我的入口点,但它不起作用,因为我定义了两个入口点,一个在app.yaml中,另一个在我的Dockerfile中。我删除了未使用的入口点,并在另一个入口点中添加了--timeout 90

对我来说,这是因为我忘记在数据库服务器上为我的 Django 设置防火墙规则。

弗兰克的回答为我指明了正确的方向。 我有一个数字海洋液滴访问托管的数字海洋Postgresql数据库。 我需要做的就是将我的快捷批处理添加到数据库的"可信来源"中。

(在 DO 控制台中单击数据库,然后单击设置。 编辑可信来源并选择快捷批处理名称(单击可编辑区域,将向您建议))。

检查您的员工是否未被健康检查杀死。长请求可能会阻止运行状况检查请求,并且工作线程会被您的平台杀死,因为平台认为工作线程没有响应。

例如,如果你有一个 25 秒长的请求,并且活动性检查配置为每 10 秒命中同一服务中的不同端点,1 秒后超时,重试 3 次,这给出了 10+1*3 ~ 13 秒,您可以看到它会触发一些时间,但并非总是如此。

如果是这种情况,解决方案是重新配置您的活动检查(或您的平台使用的任何运行状况检查机制),以便它可以等到您的典型请求完成。或者允许更多线程 - 确保运行状况检查不会被阻止足够长的时间以触发工作线程终止。

您可以看到,添加更多工作线程可能有助于(或隐藏)问题。

对我来说最简单的方法是在 app.py 所在的同一文件夹中创建一个新的 config.py 文件,并将超时和所有您想要的特殊配置放入其中:

timeout = 999

然后只需在指向此配置文件的同时运行服务器

gunicorn -c config.py --bind 0.0.0.0:5000 wsgi:app

请注意,要使此语句起作用,您还需要 wsgi.py 在同一目录中具有以下内容

from myproject import app
if __name__ == "__main__":
    app.run()

干杯!

除了已经建议的 gunicorn 超时设置之外,由于您在前面使用 nginx,您可以检查这两个参数是否有效,proxy_connect_timeout和proxy_read_timeout默认为 60 秒。可以在您的nginx配置文件中将它们设置为这样,

proxy_connect_timeout 120s;
proxy_read_timeout 120s;

就我而言,我在向服务器发送较大(10MB)文件时遇到了此问题。我的开发服务器(app.run())没有问题,但 gunicorn 无法处理它们。

对于遇到与我相同的问题的人。我的解决方案是像这样分块发送它:参考/HTML 示例,单独的大文件 参考


    def upload_to_server():
        upload_file_path = location
    
        def read_in_chunks(file_object, chunk_size=524288):
            """Lazy function (generator) to read a file piece by piece.
            Default chunk size: 1k."""
            while True:
                data = file_object.read(chunk_size)
                if not data:
                    break
                yield data
    
        with open(upload_file_path, 'rb') as f:
            for piece in read_in_chunks(f):
                r = requests.post(
                    url + '/api/set-doc/stream' + '/' + server_file_name,
                    files={name: piece},
                    headers={'key': key, 'allow_all': 'true'})

我的烧瓶服务器:


    @app.route('/api/set-doc/stream/<name>', methods=['GET', 'POST'])
    def api_set_file_streamed(name):
        folder = escape(name)  # secure_filename(escape(name))
        if 'key' in request.headers:
            if request.headers['key'] != key:                
                return ''
        else:
            return ''
        for fn in request.files:
            file = request.files[fn]
            if fn == '':
                print('no file name')
                flash('No selected file')
                return 'fail'
            if file and allowed_file(file.filename):
                file_dir_path = os.path.join(app.config['UPLOAD_FOLDER'], folder)
                if not os.path.exists(file_dir_path):
                    os.makedirs(file_dir_path)
                file_path = os.path.join(file_dir_path, secure_filename(file.filename)) 
                with open(file_path, 'ab') as f:
                    f.write(file.read())
                return 'sucess'
        return ''
如果你

更改了 Django 项目的名称,你也应该去

cd /etc/systemd/system/

然后

sudo nano gunicorn.service

然后验证绑定行末尾的应用程序名称是否已更改为新的应用程序名称

最新更新