Gunicorn: CRITICAL WORKER TIMEOUT lors de l'exécution de l'application Flask

Créé le 5 juin 2018  ·  82Commentaires  ·  Source: benoitc/gunicorn

Il semble qu'il y ait déjà eu plusieurs rapports liés à l'erreur [CRITICAL] WORKER TIMEOUT , mais cela ne cesse d'apparaître. Voici mon problème.

J'exécute cette application Flask hello world.

from flask import Flask
application = Flask(__name__)

@application.route('/')
def hello_world():
    return 'Hello, World!'

La commande gunicorn est celle-ci :

gunicorn -b 0.0.0.0:5000 --log-level=debug hello

Et voici la sortie de la console :

[2018-06-05 14:56:21 +0200] [11229] [INFO] Starting gunicorn 19.8.1
[2018-06-05 14:56:21 +0200] [11229] [DEBUG] Arbiter booted
[2018-06-05 14:56:21 +0200] [11229] [INFO] Listening at: http://0.0.0.0:5000 (11229)
[2018-06-05 14:56:21 +0200] [11229] [INFO] Using worker: sync
[2018-06-05 14:56:21 +0200] [11232] [INFO] Booting worker with pid: 11232
[2018-06-05 14:56:21 +0200] [11229] [DEBUG] 1 workers
[2018-06-05 14:56:32 +0200] [11232] [DEBUG] GET /
[2018-06-05 14:56:57 +0200] [11232] [DEBUG] Closing connection. 
[2018-06-05 14:57:16 +0200] [11232] [DEBUG] GET /
[2018-06-05 14:57:47 +0200] [11229] [CRITICAL] WORKER TIMEOUT (pid:11232)
[2018-06-05 14:57:47 +0200] [11232] [INFO] Worker exiting (pid: 11232)
[2018-06-05 14:57:47 +0200] [11324] [INFO] Booting worker with pid: 11324

Pouvez-vous s'il vous plaît expliquer clairement pourquoi j'obtiens l'erreur et si elle est attendue dans cet exemple ? Comment puis-je le corriger ou s'il s'agit d'un comportement attendu, pourquoi une erreur critique alors ?

Investigation unconfirmed

Commentaire le plus utile

L'utilisation de gunicorn avec gevent n'a pas corrigé le bug.

Tous les 82 commentaires

L'erreur n'est pas attendue, mais il n'y a rien dans votre exemple qui montre pourquoi cela se produit. Parlez-nous de votre environnement.

  • Quel client est utilisé pour se connecter à Gunicorn ?
  • Quel proxy inverse, le cas échéant, est utilisé pour se connecter à Gunicorn ?

Parlez-nous de votre environnement.

  • Quel client est utilisé pour se connecter à Gunicorn ?
    => Je viens d'envoyer une requête depuis Chromium : http://localhost :5000/
  • Quel proxy inverse, le cas échéant, est utilisé pour se connecter à Gunicorn ?
    => Pas de proxy, juste Gunicorn + Flask

Je viens de reproduire le problème sur une toute nouvelle configuration, voici les étapes :

mkdir gunicorn
cd gunicorn/
pipenv --python 3.6
pipenv install flask
pipenv install gunicorn
vim hello.py
pipenv shell
gunicorn -b 0.0.0.0:5000 --log-level=debug hello

Le hello.py est exactement la même application Flask que j'ai posté dans le rapport initial.
Ci-dessous le journal complet.

~$ gunicorn -b 0.0.0.0:5000 --log-level=debug hello
[2018-06-06 09:16:21 +0200] [19829] [DEBUG] Current configuration:
  config: None
  bind: ['0.0.0.0:5000']
  backlog: 2048
  workers: 1
  worker_class: sync
  threads: 1
  worker_connections: 1000
  max_requests: 0
  max_requests_jitter: 0
  timeout: 30
  graceful_timeout: 30
  keepalive: 2
  limit_request_line: 4094
  limit_request_fields: 100
  limit_request_field_size: 8190
  reload: False
  reload_engine: auto
  reload_extra_files: []
  spew: False
  check_config: False
  preload_app: False
  sendfile: None
  reuse_port: False
  chdir: /home/dima/work/gunicorn
  daemon: False
  raw_env: []
  pidfile: None
  worker_tmp_dir: None
  user: 1000
  group: 985
  umask: 0
  initgroups: False
  tmp_upload_dir: None
  secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
  forwarded_allow_ips: ['127.0.0.1']
  accesslog: None
  disable_redirect_access_to_syslog: False
  access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
  errorlog: -
  loglevel: debug
  capture_output: False
  logger_class: gunicorn.glogging.Logger
  logconfig: None
  logconfig_dict: {}
  syslog_addr: udp://localhost:514
  syslog: False
  syslog_prefix: None
  syslog_facility: user
  enable_stdio_inheritance: False
  statsd_host: None
  statsd_prefix: 
  proc_name: None
  default_proc_name: hello
  pythonpath: None
  paste: None
  on_starting: <function OnStarting.on_starting at 0x7f9757112d08>
  on_reload: <function OnReload.on_reload at 0x7f9757112e18>
  when_ready: <function WhenReady.when_ready at 0x7f9757112f28>
  pre_fork: <function Prefork.pre_fork at 0x7f9756c230d0>
  post_fork: <function Postfork.post_fork at 0x7f9756c231e0>
  post_worker_init: <function PostWorkerInit.post_worker_init at 0x7f9756c232f0>
  worker_int: <function WorkerInt.worker_int at 0x7f9756c23400>
  worker_abort: <function WorkerAbort.worker_abort at 0x7f9756c23510>
  pre_exec: <function PreExec.pre_exec at 0x7f9756c23620>
  pre_request: <function PreRequest.pre_request at 0x7f9756c23730>
  post_request: <function PostRequest.post_request at 0x7f9756c237b8>
  child_exit: <function ChildExit.child_exit at 0x7f9756c238c8>
  worker_exit: <function WorkerExit.worker_exit at 0x7f9756c239d8>
  nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x7f9756c23ae8>
  on_exit: <function OnExit.on_exit at 0x7f9756c23bf8>
  proxy_protocol: False
  proxy_allow_ips: ['127.0.0.1']
  keyfile: None
  certfile: None
  ssl_version: 2
  cert_reqs: 0
  ca_certs: None
  suppress_ragged_eofs: True
  do_handshake_on_connect: False
  ciphers: TLSv1
  raw_paste_global_conf: []
[2018-06-06 09:16:21 +0200] [19829] [INFO] Starting gunicorn 19.8.1
[2018-06-06 09:16:21 +0200] [19829] [DEBUG] Arbiter booted
[2018-06-06 09:16:21 +0200] [19829] [INFO] Listening at: http://0.0.0.0:5000 (19829)
[2018-06-06 09:16:21 +0200] [19829] [INFO] Using worker: sync
[2018-06-06 09:16:21 +0200] [19832] [INFO] Booting worker with pid: 19832
[2018-06-06 09:16:22 +0200] [19829] [DEBUG] 1 workers
[2018-06-06 09:16:48 +0200] [19832] [DEBUG] GET /
[2018-06-06 09:17:19 +0200] [19829] [CRITICAL] WORKER TIMEOUT (pid:19832)
[2018-06-06 09:17:19 +0200] [19832] [INFO] Worker exiting (pid: 19832)
[2018-06-06 09:17:19 +0200] [19872] [INFO] Booting worker with pid: 19872
^C[2018-06-06 09:17:26 +0200] [19829] [INFO] Handling signal: int
[2018-06-06 09:17:26 +0200] [19872] [INFO] Worker exiting (pid: 19872)
[2018-06-06 09:17:26 +0200] [19829] [INFO] Shutting down: Master
~$ pip list
Package      Version
------------ -------
click        6.7    
Flask        1.0.2  
gunicorn     19.8.1 
itsdangerous 0.24   
Jinja2       2.10   
MarkupSafe   1.0    
pip          10.0.1 
setuptools   39.2.0 
Werkzeug     0.14.1 
wheel        0.31.1

@bigunyak Je pense que c'est à cause du délai d'attente par défaut, votre travailleur est resté silencieux pendant 30 secondes. http://docs.gunicorn.org/en/stable/settings.html#timeout

Depuis votre journal,

[2018-06-05 14:57:16 +0200] [11232] [DEBUG] GET /
[2018-06-05 14:57:47 +0200] [11229] [CRITICAL] WORKER TIMEOUT (pid:11232)
[2018-06-06 09:16:48 +0200] [19832] [DEBUG] GET /
[2018-06-06 09:17:19 +0200] [19829] [CRITICAL] WORKER TIMEOUT (pid:19832)

Je constate la même chose : les travailleurs expirent même lorsqu'ils ne servent aucune demande, avec le travailleur de synchronisation.
En ce sens, le journal du niveau critique est assez déroutant.

Essayez d'utiliser Gevent worker pourrait résoudre ce problème.

En ce sens, le journal du niveau critique est assez déroutant.

Exactement, c'était ma question initiale : si c'est un comportement attendu, pourquoi une erreur critique ?
Ce serait également bien d'avoir des informations sur les raisons pour lesquelles les travailleurs doivent être redémarrés, cela pourrait peut-être être ajouté au document de conception .

Je vois cela aussi (reproduit en utilisant examples/test.py avec gunicorn test:app -b localhost:9595 --log-level=debug --timeout=5 ) et je suis d'accord que le niveau critique est un peu déroutant. Je serais d'accord pour le changer au niveau de débogage. @benoitc @tilgovi qu'en pensez-vous ?

Je pense que le niveau d'information pourrait être un peu meilleur.

J'ai eu la même chose avec MSYS2 sur Win10 mais j'ai finalement pu résoudre.

dans notify() de ...\gunicorn\workers\workertmp.py, os.fchmod est utilisé à l'origine. Mais cela ne fonctionne pas dans MSYS. Au lieu de os.fchmod, j'ai utilisé os.utime. Le code est suivi. Je pense que cela pourrait fonctionner pour toutes les plateformes.

    def notify(self):
        try:
            self.spinner = (self.spinner + 1) % 2
            os.fchmod(self._tmp.fileno(), self.spinner)
            if PLATFORM.startswith('MSYS') :
                os.utime(self._tmp.fileno(), None)
        except AttributeError:
            # python < 2.6
            self._tmp.truncate(0)
            os.write(self._tmp.fileno(), b"X")

@berkerpeksag Je ne m'attendrais pas à ce que le travailleur se

Si l'erreur persiste lorsque le travailleur n'est pas occupé, il se passe quelque chose d'autre et nous avons probablement un bogue.

[ÉDITER]
Même bug pour moi.
Avec Django 1.10 / gunicorn 19.6.0 / Python 2.7.15 en python2.7-alpine sur Debian 8.8 et le noyau d'origine 3.16, tout fonctionnait bien.
Après la mise à jour vers Django 1.11 et gunicorn 19.8.1, les travailleurs continuent d'échouer au démarrage avec [CRITICAL WORKER TIMEOUT].
La rétrogradation de gunicorn vers 19.6.0 ne résout pas le problème.
Nous avons mis à jour le noyau hôte vers la version 4.9.0 (c'était planifié) et les travailleurs ont démarré avec succès sans erreur.
Mais:

  • nous utilisons 4 travailleurs
  • après exactement 4 appels à l'application Django, les prochains appels expireront, affichant [CRITICAL WORKER TIMEOUT] dans les journaux
  • la commande linux top affiche 4 processus gunicorn bloqués avec une consommation CPU assez élevée.

Nous allons essayer gunicorn gevent pour voir si nous pouvons remettre notre application en ligne.

L'utilisation de gunicorn avec gevent n'a pas corrigé le bug.

une mise à jour sur ce problème ?

On dirait que @neocolor a identifié un vrai bug sous MSYS. Cela mériterait peut-être un autre numéro.

@bigunyak sous quelle plate-forme utilisez-vous ? J'ai essayé de reproduire avec l'exemple simple et je ne peux pas le faire en suivant exactement les étapes décrites ci-dessus. Cela correspond à mon expérience d'exécution de plusieurs applications en production sur plusieurs frameworks. Le système de notification des travailleurs n'a pas changé récemment, à ma connaissance. Ma plate-forme est Python 3.7 sur MacOS 10.13.6, mais j'exécute Gunicorn en production avec des travailleurs de synchronisation pour plusieurs applications sur Python 2.7 et 3.6.5 et je ne vois les délais d'expiration des travailleurs que lorsqu'il y a des requêtes légitimement longues qui bloquent les travailleurs.

Pour @Tberdy : que se passe-t-il si vous essayez de définir --worker-tmp-dir quelque part en dehors du système de fichiers tmpfs ? Je me demande simplement si peut-être alpin ou docker ou la combinaison interfère d'une manière ou d'une autre avec la façon dont les travailleurs avisent l'arbitre.

Voir aussi #1388 pour les problèmes de tmpfs liés à Docker.

j'ai ce problème à.

J'ai aussi ce problème, la synchronisation de gunicorn fonctionnait parfaitement jusqu'à hier soir, mais j'ai commencé à signaler, le délai d'attente des travailleurs [CRITIQUE] à l'aide de gevent semble résoudre mon problème, mais je voudrais vraiment savoir pourquoi cela s'est produit.

@timoj58 @cjmash pouvez-vous fournir plus de détails sur le problème ? Comment exécutez-vous gunicorn (dans une VM ?, options...), quel système de fichiers, OS ? Un truc qui pourrait aider à reproduire serait très utile :)

@benoitc J'exécute gunicorn pour démarrer mon projet Django sur kubernetes mes arguments gunicorn sont --bind=$port --workers=7 --timeout=1200 --log-level=debug --access-logfile - error-logfile - "

les erreurs que je reçois des journaux

```E [2018-08-09 21:47:56 +0000] [13] [INFO] Amorçage du travailleur avec pid : 13

E [2018-08-09 21:47:56 +0000] [14] [INFO] Amorçage du travailleur avec pid : 14

E [2018-08-09 21:47:56 +0000] [12] [INFO] Amorçage du travailleur avec pid : 12

E [2018-08-09 21:47:56 +0000] [1] [DEBUG] 7 travailleurs

E [2018-08-09 21:47:56 +0000] [11] [INFO] Amorçage du travailleur avec pid : 11

E [2018-08-09 21:47:55 +0000] [10] [INFO] Amorçage du travailleur avec pid : 10

E [2018-08-09 21:47:55 +0000] [9] [INFO] Boot worker avec pid: 9

E [2018-08-09 21:47:55 +0000] [8] [INFO] Boot worker avec pid: 8

E [2018-08-09 21:47:55 +0000] [1] [INFO] Utilisation du travailleur : synchroniser

E [2018-08-09 21:47:55 +0000] [1] [INFO] Écoute sur : http://0.0.0.0 :4040 (1)

E [2018-08-09 21:47:55 +0000] [1] [DEBUG] Arbiter démarré

E [2018-08-09 21:47:55 +0000] [1] [INFO] Démarrage de gunicorn 19.7.1

E raw_paste_global_conf : []

Chiffres électroniques : TLSv1

E do_handshake_on_connect : Faux

E suppress_ragged_eofs : vrai

E ca_certs : aucun

E cert_reqs : 0

E ssl_version : 2

Fichier de certificat électronique : aucun

Fichier de clé E : aucun

E proxy_allow_ips : ['127.0.0.1']

E proxy_protocol : Faux

E on_exit :

E nworkers_changed :

E worker_exit :

E child_exit :

E post_request :

E pré_requête :

E pre_exec :

E worker_abort :

E worker_int :

E post_worker_init :

E post_fork :

E pre_fork :

E when_ready :

E on_reload :

E on_starting :

E coller: Aucun

E pythonpath : aucun

E default_proc_name : art.wsgi

E proc_name : aucun

E statsd_prefix :

E statsd_host : aucun

E enable_stdio_inheritance : False

E syslog_facility : utilisateur

E syslog_prefix : aucun

E syslog : Faux

E syslog_addr : udp://localhost :514

E logconfig : aucun

E logger_class : gunicorn.glogging.Logger

E capture_output : Faux

E loglevel : débogage

Journal d'erreurs : -

E access_log_format : %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%( comme"

Journal d'accès E : -

E forwarded_allow_ips : ['127.0.0.1']

E secure_scheme_headers : {'X-FORWARDED-PROTOCOL' : 'ssl', 'X-FORWARDED-PROTO' : 'https', 'X-FORWARDED-SSL' : 'on'}

E tmp_upload_dir : aucun

E initgroups : False

E umask: 0

Groupe E : 0

Utilisateur électronique : 0

E worker_tmp_dir : aucun

Fichier Epid : Aucun

E raw_env : []

Démon E : Faux

E chdir : /usr/src/app

E sendfile : aucun

E preload_app : Faux

E check_config : Faux

E cracher : Faux

E reload_engine : automatique

E recharger : Faux

E limit_request_field_size : 8190

E limit_request_fields : 100

E limit_request_line : 4094

E keepalive: 2

E graceful_timeout : 30

E délai d'attente : 1200

E max_requests_jitter : 0

E max_requêtes : 0

E worker_connections : 1000

E fils : 1

E classe_travailleur : synchronisation

E travailleurs : 7

Carnet E : 2048

E lier : ['0.0.0.0:4040']

Configuration électronique : aucune

E [2018-08-09 21:47:55 +0000] [1] [DEBUG] Configuration actuelle :

I Aucune migration à appliquer.

I Exécution des migrations :

J'applique toutes les migrations : admin, auth, contenttypes, core, dashboard, jet, oauth2_provider, sessions

I Opérations à effectuer :

E [2018-08-09 21:47:20 +0000] [13] [INFO] Sortie du travailleur (pid: 13)

E os.path.dirname(os.path.dirname(__file__)), '.env'))

E /usr/src/app/art/wsgi.py:19 : UserWarning : Ne lit pas /usr/src/app/.env - il n'existe pas.

E [2018-08-09 21:47:00 +0000] [21] [INFO] Boot worker avec pid: 21

E [2018-08-09 21:47:00 +0000] [1] [INFO] Signal de manipulation : terme

E [2018-08-09 21:46:35 +0000] [12] [INFO] Booter le travailleur avec pid : 12

E [2018-08-09 21:46:34 +0000] [13] [INFO] Amorçage du travailleur avec pid : 13

E [2018-08-09 21:46:34 +0000] [11] [INFO] Boot worker avec pid: 11

E [2018-08-09 21:46:34 +0000] [1] [DEBUG] 7 travailleurs

E [2018-08-09 21:46:34 +0000] [10] [INFO] Amorçage du travailleur avec pid : 10

E [2018-08-09 21:46:34 +0000] [9] [INFO] Boot worker avec pid: 9

E [2018-08-09 21:46:34 +0000] [8] [INFO] Boot worker avec pid: 8

E [2018-08-09 21:46:34 +0000] [7] [INFO] Boot worker avec pid: 7

E [2018-08-09 21:46:34 +0000] [1] [INFO] Utilisation du travailleur : synchroniser

E [2018-08-09 21:46:34 +0000] [1] [INFO] Écoute sur : http://0.0.0.0 :4040 (1)

E [2018-08-09 21:46:34 +0000] [1] [DEBUG] Arbiter démarré

E [2018-08-09 21:46:34 +0000] [1] [INFO] Démarrage de gunicorn 19.7.1

E raw_paste_global_conf : []

Chiffres électroniques : TLSv1

E do_handshake_on_connect : Faux

E suppress_ragged_eofs : vrai

E ca_certs : aucun

E cert_reqs : 0

E ssl_version : 2

Fichier de certificat électronique : aucun

Fichier de clé E : aucun

E proxy_allow_ips : ['127.0.0.1']

E proxy_protocol : Faux

E on_exit :

E nworkers_changed :

E worker_exit :

E child_exit :

E post_request :

E pré_requête :

E pre_exec :

E worker_abort :

E worker_int :

E post_worker_init :

E post_fork :

E pre_fork :

E when_ready :

E on_reload :

E on_starting :

E coller: Aucun

E pythonpath : aucun

E default_proc_name : art.wsgi

E proc_name : aucun

E statsd_prefix :

E statsd_host : aucun

E enable_stdio_inheritance : False

E syslog_facility : utilisateur

E syslog_prefix : aucun

E syslog : Faux

E syslog_addr : udp://localhost :514

E logconfig : aucun

E logger_class : gunicorn.glogging.Logger

E capture_output : Faux

E loglevel : débogage

Journal d'erreurs : -

E access_log_format : %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%( comme"

Journal d'accès E : -

E forwarded_allow_ips : ['127.0.0.1']

E secure_scheme_headers : {'X-FORWARDED-PROTOCOL' : 'ssl', 'X-FORWARDED-PROTO' : 'https', 'X-FORWARDED-SSL' : 'on'}

E tmp_upload_dir : aucun

E initgroups : False

E umask: 0

Groupe E : 0

Utilisateur électronique : 0

E worker_tmp_dir : aucun

Fichier Epid : Aucun

E raw_env : []

Démon E : Faux

E chdir : /usr/src/app

E sendfile : aucun

E preload_app : Faux

E check_config : Faux

E cracher : Faux

E reload_engine : automatique

E recharger : Faux

E limit_request_field_size : 8190

E limit_request_fields : 100

E limit_request_line : 4094

E keepalive: 2

E graceful_timeout : 30

E délai d'attente : 1200

E max_requests_jitter : 0

E max_requêtes : 0

E worker_connections : 1000

E fils : 1

E classe_travailleur : synchronisation

E travailleurs : 7

Carnet E : 2048

E lier : ['0.0.0.0:4040']

Configuration électronique : aucune

E [2018-08-09 21:46:34 +0000] [1] [DEBUG] Configuration actuelle :

```

J'ai un peu galéré pour reproduire le problème cette fois mais il est toujours là dans la dernière version 19.9.0 de gunicorn.
Les étapes pour le reproduire sont exactement les mêmes que celles que j'ai décrites dans ce post .
Mon système est Arch Linux x86_64 GNU/Linux (noyau 4.17.2-1-ARCH), Python 3.6.5
Voici une nouvelle trace de journal.

~$ gunicorn -b 0.0.0.0:5000 --log-level=debug hello
[2018-08-10 09:48:40 +0200] [23114] [DEBUG] Current configuration:
  config: None
  bind: ['0.0.0.0:5000']
  backlog: 2048
  workers: 1
  worker_class: sync
  threads: 1
  worker_connections: 1000
  max_requests: 0
  max_requests_jitter: 0
  timeout: 30
  graceful_timeout: 30
  keepalive: 2
  limit_request_line: 4094
  limit_request_fields: 100
  limit_request_field_size: 8190
  reload: False
  reload_engine: auto
  reload_extra_files: []
  spew: False
  check_config: False
  preload_app: False
  sendfile: None
  reuse_port: False
  chdir: /home/dima/lerning/python/modules/gunicorn
  daemon: False
  raw_env: []
  pidfile: None
  worker_tmp_dir: None
  user: 1000
  group: 985
  umask: 0
  initgroups: False
  tmp_upload_dir: None
  secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
  forwarded_allow_ips: ['127.0.0.1']
  accesslog: None
  disable_redirect_access_to_syslog: False
  access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
  errorlog: -
  loglevel: debug
  capture_output: False
  logger_class: gunicorn.glogging.Logger
  logconfig: None
  logconfig_dict: {}
  syslog_addr: udp://localhost:514
  syslog: False
  syslog_prefix: None
  syslog_facility: user
  enable_stdio_inheritance: False
  statsd_host: None
  statsd_prefix: 
  proc_name: None
  default_proc_name: hello
  pythonpath: None
  paste: None
  on_starting: <function OnStarting.on_starting at 0x7f5f8b9dad08>
  on_reload: <function OnReload.on_reload at 0x7f5f8b9dae18>
  when_ready: <function WhenReady.when_ready at 0x7f5f8b9daf28>
  pre_fork: <function Prefork.pre_fork at 0x7f5f8b4ec0d0>
  post_fork: <function Postfork.post_fork at 0x7f5f8b4ec1e0>
  post_worker_init: <function PostWorkerInit.post_worker_init at 0x7f5f8b4ec2f0>
  worker_int: <function WorkerInt.worker_int at 0x7f5f8b4ec400>
  worker_abort: <function WorkerAbort.worker_abort at 0x7f5f8b4ec510>
  pre_exec: <function PreExec.pre_exec at 0x7f5f8b4ec620>
  pre_request: <function PreRequest.pre_request at 0x7f5f8b4ec730>
  post_request: <function PostRequest.post_request at 0x7f5f8b4ec7b8>
  child_exit: <function ChildExit.child_exit at 0x7f5f8b4ec8c8>
  worker_exit: <function WorkerExit.worker_exit at 0x7f5f8b4ec9d8>
  nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x7f5f8b4ecae8>
  on_exit: <function OnExit.on_exit at 0x7f5f8b4ecbf8>
  proxy_protocol: False
  proxy_allow_ips: ['127.0.0.1']
  keyfile: None
  certfile: None
  ssl_version: 2
  cert_reqs: 0
  ca_certs: None
  suppress_ragged_eofs: True
  do_handshake_on_connect: False
  ciphers: TLSv1
  raw_paste_global_conf: []
[2018-08-10 09:48:40 +0200] [23114] [INFO] Starting gunicorn 19.9.0
[2018-08-10 09:48:40 +0200] [23114] [DEBUG] Arbiter booted
[2018-08-10 09:48:40 +0200] [23114] [INFO] Listening at: http://0.0.0.0:5000 (23114)
[2018-08-10 09:48:40 +0200] [23114] [INFO] Using worker: sync
[2018-08-10 09:48:40 +0200] [23117] [INFO] Booting worker with pid: 23117
[2018-08-10 09:48:40 +0200] [23114] [DEBUG] 1 workers
[2018-08-10 09:48:45 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:48:54 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:49:00 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:49:18 +0200] [23117] [DEBUG] Closing connection. 
[2018-08-10 09:49:18 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:49:23 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:49:37 +0200] [23117] [DEBUG] Closing connection. 
[2018-08-10 09:49:37 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:49:50 +0200] [23117] [DEBUG] Closing connection. 
[2018-08-10 09:51:11 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:51:13 +0200] [23117] [DEBUG] GET /
[2018-08-10 09:51:43 +0200] [23114] [CRITICAL] WORKER TIMEOUT (pid:23117)
[2018-08-10 09:51:43 +0200] [23117] [INFO] Worker exiting (pid: 23117)
[2018-08-10 09:51:44 +0200] [23229] [INFO] Booting worker with pid: 23229

Comme auparavant, pour le tester, je venais de frapper http://0.0.0.0 :5000/ dans Chromium.
Vous trouverez ci-dessous le contenu de Pipfile et Pipfile.lock pour que vous puissiez voir l'environnement exact.

  • Pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
flask = "*"
gunicorn = "*"

[dev-packages]

[requires]
python_version = "3.6"
  • Pipfile.lock
{
    "_meta": {
        "hash": {
            "sha256": "81cb5d5f0b11719d8d9c5ec9cc683fdcf959c652fda256d5552a82d0f459a99c"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.6"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "click": {
            "hashes": [
                "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d",
                "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b"
            ],
            "version": "==6.7"
        },
        "flask": {
            "hashes": [
                "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48",
                "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"
            ],
            "index": "pypi",
            "version": "==1.0.2"
        },
        "gunicorn": {
            "hashes": [
                "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471",
                "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3"
            ],
            "index": "pypi",
            "version": "==19.9.0"
        },
        "itsdangerous": {
            "hashes": [
                "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
            ],
            "version": "==0.24"
        },
        "jinja2": {
            "hashes": [
                "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
                "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
            ],
            "version": "==2.10"
        },
        "markupsafe": {
            "hashes": [
                "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
            ],
            "version": "==1.0"
        },
        "werkzeug": {
            "hashes": [
                "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
                "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
            ],
            "version": "==0.14.1"
        }
    },
    "develop": {}
}

Juste pour info, je vois aussi cet échec très régulièrement avec :

nohup gunicorn -w 8 --access-logfile=- --bind 127.0.0.1:5000 wsgi &

J'ai juste un wsgi.py qui a

from chart_api import application
if __name__ == "__main__":
    application.run()

Faites-moi savoir s'il y a quelque chose que vous voulez que j'essaye/expérimente ou s'il y a des détails dans les journaux que vous voulez que je vérifie. J'exécute Flask sur une VM GCP.

désolé pour la réponse tardive. je le lance comme

gunicorn --log-file=/home/ubuntu/log/gunicorn.log predictor_ api:app -b localhost:5000 &

j'ai utilisé le paramètre gevent, etc., mais j'ai modifié ma conception de ce pour quoi j'avais besoin de cela afin de contourner le problème, d'où le paramètre de base ci-dessus (qui a également échoué, mais il faut s'y attendre étant donné l'absence de gevent)

Python version 3.6
env : aws tensorflow_p36 optimisé (ubuntu)
nginx est assis devant gunicorn, qui exécute une application de flacon.

flacon version 1.0.2
nginx version 1.10.3
gunicorne version 19.9.0

J'ai également modifié les délais d'attente de nginx au cas où cela aurait pu en être la cause.

Face au même problème avec le serveur gunicorn

# gunicorn Applicat ionServer:app -b 0.0.0.0:6001 -w 8 --threads 4 --backlog 2048 \
# --timeout 120 --graceful-timeout 60 --access-logfile logs/access.log \

--error-logfile logs/error.log --log-level=info

Flacon==0.12.1
gunicorne==19.7.1

lorsque je démarre le serveur avec la commande ci-dessus, mon système se bloque pendant un certain temps et les pids des travailleurs continuent de démarrer, bien que j'ai gardé le délai d'attente de 120 secondes et que le serveur n'accepte pas les demandes uniques.

Une mise à jour sur ce problème ? j'ai le même problème

DÉLAI D'ATTENTE DU TRAVAILLEUR [CRITIQUE]

Vous vous demandez si quelqu'un a réussi à reproduire cela dans une image Docker ?

Voir également cela lorsque vous essayez d'implémenter le travailleur ddtrace-run de datadog sur une application existante en commençant par gunicorn -k gevent --threads 4.

Drôle de trace d'un SystemExit que je n'ai jamais vu auparavant aussi...
[2018-11-07 11:11:50 +0000] [15987] [INFO] Booting worker with pid: 15987 [2018-11-07 11:11:50 +0000] [15977] [DEBUG] 1 workers [2018-11-07 11:12:20 +0000] [15977] [CRITICAL] WORKER TIMEOUT (pid:15987) Exception SystemExit: 1 in <bound method LibevLoop._loop_will_run of <cassandra.io.libevreactor.LibevLoop object at 0x7f3cb66d4a50>> ignored

Je suis en mesure de résoudre ce problème en faisant correspondre le nombre de travailleurs et le nombre de threads.

J'avais défini workers = (2 * cpu_count) + 1 et je n'avais pas défini de threads.

Une fois que j'ai modifié threads = workers , tout a bien fonctionné. Juste au cas où, si cela aide quelqu'un.

Voilà à quoi ça ressemble maintenant

def run(host='0.0.0.0', port=8080, workers=1 + (multiprocessing.cpu_count() * 2)):
    """Run the app with Gunicorn."""

    if app.debug:
        app.run(host, int(port), use_reloader=False)
    else:
        gunicorn = WSGIApplication()
        gunicorn.load_wsgiapp = lambda: app
        gunicorn.cfg.set('bind', '%s:%s' % (host, port))
        gunicorn.cfg.set('workers', workers)
        gunicorn.cfg.set('threads', workers)
        gunicorn.cfg.set('pidfile', None)
        gunicorn.cfg.set('worker_class', 'sync')
        gunicorn.cfg.set('keepalive', 10)
        gunicorn.cfg.set('accesslog', '-')
        gunicorn.cfg.set('errorlog', '-')
        gunicorn.cfg.set('reload', True)
        gunicorn.chdir()
        gunicorn.run()

J'ai rencontré ce problème en exécutant Django avec un seul conteneur Docker sur AWS Elastic Beanstalk. J'ai résolu le problème en corrigeant mes groupes de sécurité pour m'assurer que mon instance EC2 pouvait parler à mon instance RDS. Je reconnais que ce n'est peut-être pas la solution pour 99% des gens sur ce problème, mais je laisse cette note pour aider les autres à éviter de perdre des heures à tomber dans ce terrier de lapin.

Dans mon cas, le problème a été résolu en effectuant systemctl daemon-reload .
Pour faire court, je connaissais le délai d'attente par défaut à 30 secondes, mais je n'ai pas pu le changer jusqu'à ce que je me demande que je devais bien sûr recharger le démon systemd pour appliquer les modifications au service.

@bigunyak @benoitc @tilgovi
Après une longue poursuite Google Goose et quelques expérimentations, je pense que la cause première de ce problème est le "service de préconnexion/prédiction" de chrome (qui est activé par défaut dans Chrome et Chromium).

@jeiting a fait un bon article sur ce problème
https://hackernoon.com/chrome-preconnect-breaks-singly-threaded-servers-95944be16400
Quelques lectures supplémentaires :
https://github.com/corydolphin/flask-cors/issues/147
https://github.com/pallets/flask/issues/2169

TLDR
Dans certains cas, Chrome/Chromium s'ouvre et contient une ou plusieurs connexions TCP « vides » (c'est-à-dire qu'il prédit qu'il va bientôt récupérer une autre ressource). Si la ou les connexions TCP "vides" atteignent d'abord votre serveur gunicorn, toute demande "réelle" ultérieure de Chrome peut rester bloquée derrière la demande "vide" jusqu'à ce que le ou les travailleurs traitant la demande "vide" expirent. Cela est plus susceptible de se produire si vous n'exécutez qu'un seul travailleur de synchronisation dans gunicorn. Mais comme mes expériences l'ont montré, cela peut arriver même si vous exécutez plusieurs travailleurs de synchronisation.

Mon environnement

  • Mon système d'exploitation natif est Ubuntu 17
  • J'ai une API de repos qui s'exécute dans un conteneur Docker local. Lignes pertinentes du Dockefile
FROM ubuntu:18.04
...
RUN pip3 install Flask==1.0.2
RUN pip3 install gunicorn==19.9.0
RUN pip3 install flask-cors==3.0.6
......
  • J'ai 3 navigateurs installés : Firefox 61.0.1, Chromium 67.0.3396.99, Chrome 70.0.3538.102
  • J'ai une application de réaction qui est servie à partir d'un conteneur Docker différent sur un port différent
  • L'application React envoie des requêtes CORS AJAX à mon API (ce sont des requêtes CORS car le javascript est servi sur un port localhost tout en envoyant des appels API à un autre port localhost)
  • La requête qui est toujours "bloquée" dans mes expériences est une requête CORS OPTIONS (déclenchée par le navigateur pour demander l'autorisation d'effectuer ensuite une requête POST). Il semble logique qu'un navigateur essaie d'établir des connexions prédictives avec l'appel OPTIONS, car il sera probablement suivi d'un appel POST.

Expérience 1
Configuration de Gunicorn : 1 travailleur de synchronisation (délai d'expiration par défaut de 30 s)

Firefox : à presque chaque chargement de page de réaction, la requête CORS OPTIONS est bloquée pendant 5 secondes, puis réussit.
Chrome : À CHAQUE chargement de page de réaction, la demande CORS OPTIONS est bloquée pendant 1,5 minute !!!! puis réussit.
Chrome (service de prédiction désactivé) : tout se charge correctement
Chrome : tout se charge bien

Expérience 2
Configuration de Gunicorn : 4 travailleurs de synchronisation (délai d'expiration par défaut de 30 s)

Firefox : tout se charge bien
Chrome : à chaque troisième chargement de page de réaction, la demande CORS OPTIONS est bloquée pendant 30 s, puis réussit.
Chrome (service de prédiction désactivé) : tout se charge correctement
Chrome : tout se charge bien

Expérience 3
Configuration de Gunicorn : 8 travailleurs de synchronisation (délai d'expiration par défaut de 30 s)

Firefox : tout se charge bien
Chrome : tout se charge bien
Chrome : tout se charge bien

Expérience 4
Exécutez le serveur de développement Flask avec threaded=True

Firefox : tout se charge bien
Chrome : tout se charge bien
Chrome : tout se charge bien

Résumé de l'expérience

  • Firefox semble également avoir un service de prédiction, mais il semble plus gracieux. (Expérience 1)
  • Le chrome semble le plus agressif dans la création de connexions "prédictives". Il semble lancer jusqu'à 3-4 connexions "de prédiction" lors d'une requête OPTIONS (Expérience 1 et 2)
  • Étonnamment, même si Chrome a également activé le service de prédiction, cela n'a causé aucun problème dans aucune des expériences (peut-être que l'ordre ou le nombre de connexions "de prédiction" est différent de Chromium)

Solution
En production : la solution la plus simple consiste à mettre nginx devant gunicorn. Si vous voulez comprendre pourquoi nginx résout ce problème, voici un bel article qui l'explique : https://www.brianstorti.com/the-role-of-a-reverse-proxy-to-protect-your-application-against -clients-lents/

En développement : la solution la plus simple consiste à exécuter le serveur de développement flask avec threaded=True . Ou vous pouvez désactiver le service de prédiction dans Chrome/Chromium.

améliorations de débogage gunicorn
Pour aider à déboguer un problème comme celui-ci à l'avenir, je pense que c'est une bonne idée d'ajouter des instructions de journal de débogage à côté des appels select() et accept() dans le travailleur de synchronisation.
https://github.com/benoitc/gunicorn/blob/e974f30517261b2bc95cfb2017a8688f367c8bf3/gunicorn/workers/sync.py#L26
https://github.com/benoitc/gunicorn/blob/e974f30517261b2bc95cfb2017a8688f367c8bf3/gunicorn/workers/sync.py#L34
Cela montrerait qu'un travailleur a accepté une nouvelle connexion TCP, mais ne reçoit aucune donnée.

@asnisarenko super article, merci. #1929 est un autre cas inhabituel d'un client lent qui produit un symptôme similaire dans le serveur à thread unique - dans ce cas, la négociation TLS vers un port non TLS ressemble à un client à écriture lente, car il ne parvient pas à envoyer rapidement des en-têtes sensibles .

Il se peut que ce soit le Gunicorn, car les travailleurs de synchronisation à thread unique auront besoin d'un nouveau paramètre ajustable, d'une suppression client agressive pour les clients qui n'envoient pas au moins une première ligne d'en-tête de requête, sinon tous les en-têtes, dans un délai raisonnable.

Si cela se produit dans votre application, vous pouvez ajouter un code de diagnostic temporaire :

def trace_on_abort():
    import signal
    import traceback

    def print_trace(sig, frame):
        print(''.join(traceback.format_stack(frame)))

    signal.signal(signal.SIGABRT, print_trace)

Espérons que cela devrait révéler où votre application se bloque.

Par exemple, j'ai réussi à import flask avant gevent.monkey.patch_all() et donc le app._before_request_lock mon Flask a fini par être un verrou non gevent (c'est-à-dire sans patch threading.Lock ). Dans un tel cas, deux requêtes rapprochées au démarrage de l'application entraîneraient son blocage (2e requête verrouillerait l'intégralité du thread). Mais c'était mon bug, et votre bug peut différer.

@asnisarenko Je ne sais pas pourquoi vous rencontrez un blocage. Vous n'avez absolument _pas_ besoin d'un travailleur par connexion, car chaque travailleur peut gérer en coopération des milliers de connexions (tant que le code de gestion ne monopolise pas le processeur / verrouille l'intégralité du thread). Plus précisément, si le navigateur bloque la requête, cela signifie simplement que lorsque gunicorn read() s à partir du socket, il bloquera un greenlet, mais puisque read devrait être patché par un singe, il cédera à les autres greenlets.

@ikonst
Ce problème parle de l'exécution de gunicorn avec la configuration par défaut. La classe de travailleurs par défaut est sync http://docs.gunicorn.org/en/stable/settings.html#worker -class

sync travailleurs

Je ne sais pas ce qui m'a causé cela, mais le passage du type de travailleur sync par défaut à eventlet résolu le problème :

CMD pipenv run gunicorn webapp -b 0.0.0.0:8080 -k eventlet

Bonne chance.

Peut-être que cela ne va m'aider que moi, mais comme il m'a fallu 7h pour déboguer ce problème de mon côté (une application Flask/Gunicorn fonctionnant dans Docker sur EC2), si je peux épargner ce mal de tête à quelques personnes, c'est une petite victoire.

La limite de mémoire du conteneur était trop faible.

Si j'ai des fuites de mémoire à l'avenir, j'essaierai de mettre une limite de mémoire plus élevée, mais pour l'instant, aucune limite ne fera l'affaire.

Merci

def trace_on_abort():
    import signal
    import traceback

    def print_trace(sig, frame):
        print(''.join(traceback.format_stack(frame)))

    signal.signal(signal.SIGABRT, print_trace)

Vous m'aidez beaucoup. EHU )))

Où va cette méthode trace_on_abort() ? Dans le fichier de code de l'application ? A-t-il besoin d'une annotation ?

@mattg-vbt ajoutez ceci au début de votre application lorsque vous devez déboguer et supprimez-le une fois le débogage terminé

@ikonst J'ai ajouté ceci à mon application, mais je ne le vois pas être touché. J'obtiens le délai d'attente du travailleur, mais cette méthode n'est pas touchée.

@mattg-vbt pouvez-vous essayer de faire kill -ABRT $pid pour voir la fonction print_trace appelée ? (l'idée est que vous obtiendrez SIGABRT de werkzeug lorsque votre travailleur expire, mais vérifions d'abord qu'il est appelé du tout)

@asnisarenko pour les moins avertis d'entre nous, comment puis-je mettre à jour le procfile ?

web gunicorn app:app -k gevent --worker-connections 1000 ?

@SumNeuron
Si vous utilisez des travailleurs gevent (au lieu de travailleurs de synchronisation), vous ne devriez pas rencontrer ce problème. Donc la commande que vous avez postée semble correcte.

Je ne rencontre ce problème avec les travailleurs gevent qu'à l'initialisation, ce qui est un peu ennuyeux car je traite certaines tâches avant de lancer mon application. Mais je fixe un délai d'attente élevé pour le moment.
J'ai posté un dépôt de test reproductible ici : https://github.com/zamponotiropita/test-gunicorn-worker-timeout -> test_0 échoue, test_1 et test_2 réussissent

@zamponotiropita Essayez-vous de le faire par travailleur ou par application avant le bifurcation ?

@ikonst s'il vous plaît vérifier le fichier d'exécution run.sh , c'est par travailleur. Le problème ne s'est pas posé avec le préchargement, mais j'ai eu des problèmes de connexion à la base de données lors du préchargement, car l'objet d'application (et avec lui la connexion à la base de données) est copié et transmis les processus lors du fork -> ma base de données ne peut pas gérer connexions identiques du maître et des travailleurs, et je n'ai pas pu trouver de solution de contournement

J'ai un problème similaire. J'essaie de générer beaucoup de données à la volée avec Flask en utilisant
cette méthode, et elle échoue car le travailleur expire après l'expiration de ce qui a été défini dans --timeout . Un exemple minimal à reproduire :

test_gunicorn_timeout.py

import flask
from time import sleep


app = flask.Flask(__name__)


@app.route('/')
def gunicorn_timeout():
    def generator():
        for _ in range(10):
            yield b'Yet another line...'
            sleep(2)
    return flask.Response(generator(), mimetype='text/plain')

Ensuite, exécutez gunicorn --timeout 10 test_gunicorn_timeout:app et lorsque vous demandez localhost:8000 après 10 secondes, vous obtenez un
[CRITICAL] WORKER TIMEOUT .

J'ai aussi essayé de courir avec -k gevent et -k eventlet et rien n'a changé.

Je suis sous windows 10 pro. Avec docker compose
gunicorn app -b 0.0.0.0:8000 -k gevent
A travaillé pour moi en installant gevent dans mon conteneur python
Comment installer gevent

Ensuite, exécutez gunicorn --timeout 10 test_gunicorn_timeout:app et lorsque vous demandez localhost:8000 après 10 secondes, vous obtenez un
[CRITICAL] WORKER TIMEOUT .

J'ai aussi essayé de courir avec -k gevent et -k eventlet et rien n'a changé.

@ltskv

avez-vous trouvé une solution au problème [CRITICAL] WORKER TIMEOUT ?

J'ai un problème similaire avec un point de terminaison de streaming de flacon que j'essaie de déployer sur heroku (qui utilise gunicorn). Là, l'interface proxy est censée maintenir la connexion active tant que certaines données sont envoyées dans les 30 secondes, mais il semble que gunicorn tue simplement le processus s'il ne s'est pas terminé dans les 30 secondes, même s'il est toujours en cours d'exécution et produit des données.

La documentation gunicorn n'est pas tout à fait claire pour moi sur ce point. Pour --timeout, il indique que Workers silent for more than this many seconds are killed and restarted. Mais il semble que les travailleurs soient tués après 30 secondes même s'ils produisent toujours des données ?

La documentation gunicorn n'est pas tout à fait claire pour moi sur ce point. Pour --timeout, il est indiqué que les travailleurs silencieux pendant plus de ce nombre de secondes sont tués et redémarrés. Mais il semble que des travailleurs soient tués après 30 secondes alors qu'ils produisent encore des données ?

@kurt-hectic que la documentation devrait être améliorée. Par silencieux, nous entendons silencieux du point de vue du processus d'arbitrage, qui communique avec les travailleurs via un fichier temporaire. Si le travailleur est occupé à envoyer des données, il ne met pas à jour ce fichier. Du point de vue de l'arbitre, le travailleur manque de battements de cœur.

Voir aussi #1974.

En utilisant n'importe quel travailleur autre que celui par défaut, le travailleur synchrone devrait atténuer le problème de la diffusion en continu de corps volumineux, car les autres travailleurs feront battre le fichier temporaire même lorsqu'ils traitent des demandes.

@kurt-hectic J'ai revérifié une fois de plus l'option -k gevent et inséré sleep(0) entre les itérations du générateur et cela a fonctionné pour moi (je ne sais pas pourquoi cela n'a pas fonctionné à l'époque J'ai posté la question cependant).

--timeout=5

C'est la cause la plus fréquente de ce problème.

J'espère que ma solution pourra vous aider. J'ai rencontré ce problème critique de délai d'attente de travail il y a quelques jours et j'ai essayé quelques solutions. Cela fonctionne maintenant bien.

Voici ma compréhension et mes solutions :

  1. Essayez le préchargement dans gunicorn

Il ne parvient pas à démarrer les travailleurs car il a besoin de plus de temps pour charger le package, tel que le backend tensorflow, pour démarrer le service. Ainsi, lorsque vous rencontrez un temps de démarrage de l'application lent, essayez d'activer l'option de préchargement dans gunicorn (voir https://devcenter.heroku.com/articles/python-gunicorn#advanced-configuration).

gunicorn hello:app --preload

  1. Essayez d'augmenter le délai d'attente pour gunicorn

Le délai d'attente par défaut est de 30 s. Si votre application a vraiment besoin de beaucoup de temps pour terminer une API, augmentez le délai d'expiration.

gunicorn hello:app --timeout 10

Cependant, de mon point de vue, cela n'a pas de sens si une API a besoin de plus d'une minute pour se terminer. Si c'est le cas, essayez de faire des progrès dans votre code.

  1. Si vous utilisez k8s, vous pouvez également définir un délai d'attente en secondes pour votre conteneur/image dans yaml.

J'ai rencontré le même problème aujourd'hui. Dans mon cas, l'API prenait environ une minute pour calculer les données et les renvoyer au client, ce qui entraînait des erreurs CRITICAL WORKER TIMEOUT. Je l'ai résolu en augmentant le délai d'attente pour gunicorn à plus d'une minute - cela a fonctionné, je n'ai pas vu le problème revenir. J'espère que cela t'aides. J'utilise uvicorn.workers.UvicornWorker.

J'ai corrigé ce problème en ajoutant des travailleurs supplémentaires à gnuicorn :

web: gunicorn --workers=3 BlocAPI:app --log-file -

Aucune idée pourquoi.

Peut-être avez-vous eu une impasse ? Votre application se fait-elle des requêtes ?

Le dimanche 5 janvier 2020, 10:52 alpinechicken, [email protected] a écrit :

J'ai corrigé ce problème en ajoutant des travailleurs supplémentaires à gnuicorn :

web: gunicorn --workers=3 BlocAPI:app --log-file -

Aucune idée pourquoi.

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/1801?email_source=notifications&email_token=AAAEQJVQRCW3C63EZJWIN5DQ4G3WTA5CNFSM4FDLD5PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXWWH16PZGO
ou se désinscrire
https://github.com/notifications/unsubscribe-auth/AAAEQJXZM4NLK56DZMFSZALQ4G3WTANCNFSM4FDLD5PA
.

Oui, une route en appelle une autre - est-ce mauvais ?

Cela signifie que vous avez besoin d'au moins deux travailleurs, sinon votre serveur
impasse. La requête attendra que le serveur réponde au deuxième
demande (qui serait mise en file d'attente).

Vous obtenez une demande simultanée par travailleur.

Le lundi 6 janvier 2020, 02:45 alpinechicken, [email protected] a écrit :

Oui, une route en appelle une autre - est-ce mauvais ?

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/1801?email_source=notifications&email_token=AAAEQJSFEFBBI6AMZJCM4C3Q4KLOJA5CNFSM4FDLD5PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX5CNFSM4FDLD5PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVEX98WWHJLOZDNGO,GO
ou se désinscrire
https://github.com/notifications/unsubscribe-auth/AAAEQJXTCPOFIZJU5PUPOODQ4KLOJANCNFSM4FDLD5PA
.

Ah ça a du sens. Merci!

Le mardi 7 janvier 2020 à 6 h 23, bobf [email protected] a écrit :

Cela signifie que vous avez besoin d'au moins deux travailleurs, sinon votre serveur
impasse. La requête attendra que le serveur réponde au deuxième
demande (qui serait mise en file d'attente).

Vous obtenez une demande simultanée par travailleur.

Le lundi 6 janvier 2020, 02:45 alpinechicken, [email protected] a écrit :

Oui, une route en appelle une autre - est-ce mauvais ?

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub
<
https://github.com/benoitc/gunicorn/issues/1801?email_source=notifications&email_token=AAAEQJSFEFBBI6AMZJCM4C3Q4KLOJA5CNFSM4FDLD5PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVEI98WWHJLOZDNGO5
,
ou se désinscrire
<
https://github.com/notifications/unsubscribe-auth/AAAEQJXTCPOFIZJU5PUPOODQ4KLOJANCNFSM4FDLD5PA

.

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/1801?email_source=notifications&email_token=AAH2WRPVPVO2EJ53BKQW5B3Q4OHLRA5CNFSM4FDLD5PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBJGOcomLNMVWXH90
ou se désinscrire
https://github.com/notifications/unsubscribe-auth/AAH2WRM2LLIB4O6OHCU5UG3Q4OHLRANCNFSM4FDLD5PA
.

classe_travailleur', 'synchronisation')

Je suis en mesure de résoudre ce problème en faisant correspondre le nombre de travailleurs et le nombre de threads.

J'avais défini workers = (2 * cpu_count) + 1 et je n'avais pas défini de threads.

Une fois que j'ai modifié threads = workers , tout a bien fonctionné. Juste au cas où, si cela aide quelqu'un.

Voilà à quoi ça ressemble maintenant

def run(host='0.0.0.0', port=8080, workers=1 + (multiprocessing.cpu_count() * 2)):
    """Run the app with Gunicorn."""

    if app.debug:
        app.run(host, int(port), use_reloader=False)
    else:
        gunicorn = WSGIApplication()
        gunicorn.load_wsgiapp = lambda: app
        gunicorn.cfg.set('bind', '%s:%s' % (host, port))
        gunicorn.cfg.set('workers', workers)
        gunicorn.cfg.set('threads', workers)
        gunicorn.cfg.set('pidfile', None)
        gunicorn.cfg.set('worker_class', 'sync')
        gunicorn.cfg.set('keepalive', 10)
        gunicorn.cfg.set('accesslog', '-')
        gunicorn.cfg.set('errorlog', '-')
        gunicorn.cfg.set('reload', True)
        gunicorn.chdir()
        gunicorn.run()

Selon la doc gunicorn, il change la classe de travail de sync à gthread si plusieurs threads sont mentionnés.
PS : -
Si vous essayez d'utiliser le type de travailleur sync et définissez le paramètre de threads sur plus de 1, le type de travailleur gthread sera utilisé à la place.

Mon cas:

Environnement : Ubuntu18.04+ gunicorn+ nginx +flask

pip installer gunicorn[gevent] dans mon environnement virtuel

Remplacez gunicorn -b localhost:8000 -w 4 web:app par gunicorn -b localhost:8000 -k gevent web:app

Ça marche.

Merci à tous ceux qui ont tant fait pour s'entraider à résoudre leurs problèmes. Veuillez continuer à publier sur ce problème si cela vous semble approprié.

Cependant, je ferme ce problème car je ne pense pas qu'il y ait de bogue dans Gunicorn ici et je ne pense pas qu'il y ait une action à prendre, même si je serai heureux d'aider à revoir les PR qui essaient d'ajouter de la documentation pour cela d'une manière ou d'une autre ou d'améliorer messages de journal.

S'il vous plaît, ne vous méprenez pas sur mon intention. Si vous suspectez un bogue dans Gunicorn et que vous souhaitez continuer à discuter, veuillez le faire. De préférence, ouvrez un nouveau ticket avec un exemple d'application qui reproduit votre problème. Cependant, à ce stade, il y a trop de problèmes, de résolutions et de conversations différents dans ce problème pour qu'il soit très lisible.

Si vous exécutez Gunicorn sans proxy inverse de mise en mémoire tampon devant vous, vous obtiendrez des délais d'expiration par défaut, synchronisez le travailleur pour un certain nombre de raisons. Les plus communs sont :

  • Clients lents
  • Connexions de pré-connexion / pré-extraction laissées ouvertes par les navigateurs et les proxys
  • Réponses longues dues à l'appel d'API externes ou à de nombreuses tâches liées au processeur

Vous pouvez basculer vers des types de travail asynchrones ou threadés, ou vous pouvez placer Gunicorn derrière un proxy inverse de mise en mémoire tampon. Si vous savez que vos délais d'expiration sont dus au fait que votre propre code effectue des appels lents à des API externes ou effectue un travail important auquel vous vous attendez, vous pouvez augmenter l'option --timeout .

Cela signifie que vous avez besoin d'au moins deux travailleurs, sinon votre serveur se bloquera. La demande attendra que le serveur réponde à la deuxième demande (qui serait mise en file d'attente). Vous obtenez une demande simultanée par travailleur.

Le lun. 6 janv. 2020, 02:45 alpinechicken, @ . * > a écrit : Oui, une route en appelle une autre - est-ce mauvais ?

Est-ce le cas lors de l'appel de la fonction « redirect » comme valeur de retour pour une route ?

Est-ce le cas lors de l'appel de la fonction « redirect » comme valeur de retour pour une route ?

Non. Une redirection de flacon répond par une redirection HTTP et le travailleur est alors libre d'accepter une nouvelle demande. Le client fait une autre demande lorsqu'il voit cette réponse et chaque fois qu'un travailleur est prêt, il recevra cette demande.

J'ai corrigé ce problème en ajoutant des travailleurs supplémentaires à gnuicorn :

web: gunicorn --workers=3 BlocAPI:app --log-file -

Aucune idée pourquoi.

Est-ce lié au commentaire précédent de @anilpai où il a défini workers=1 + (multiprocessing.cpu_count() * 2) .. ?

J'ai eu un problème similaire à celui-ci. Il s'avère que j'ai eu une erreur dans mon point d'entrée à l'application. D'après le débogage, il semblait que je lançais essentiellement une application de flacon à partir de gunicorn, dont les travailleurs entrent ensuite dans une boucle de connexion infinie qui expire toutes les 30 secondes.

Je suis sûr que cela n'affecte pas tous les utilisateurs ci-dessus, mais pourrait bien en affecter certains.

Dans mon fichier module/wsgi.py que j'exécute avec gunicorn module.wsgi j'avais -

application = my_create_app_function()
application.run(host="0.0.0.0")

Alors que j'aurais dû avoir -

application = my_create_app_function()
if __name__ == "__main__":
     application.run(host="0.0.0.0")

Essentiellement, vous ne voulez pas appeler application.run() lorsque vous utilisez gunicorn. Le __name__ sous gunicorn ne sera pas "__main__" , mais il le sera dans Flask, vous pouvez donc toujours déboguer localement.

Je n'ai pas pu trouver de référence à cela dans la documentation de gunicorn, mais je pourrais imaginer qu'il s'agit d'un cas d'erreur courant, alors peut-être qu'un avertissement est nécessaire.

Cela se produit toujours. L'ajout de --preload à l'appel Gunicorn a résolu le problème pour moi.

Ce bug n'est toujours pas corrigé ? J'observe exactement ce comportement.

Gunicorn commence comme ceci dans systemd :

[Service]
PIDFile = /run/gunicorn.pid
WorkingDirectory = /home/pi/pyTest
ExecStart=/usr/local/bin/gunicorn  app:app  -b 0.0.0.0:80 --pid /run/gunicorn.pid
RuntimeDirectory=/home/pi/pyTest
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all
User=root
Group=root
ExecReload = /bin/kill -s HUP $MAINPID
ExecStop = /bin/kill -s TERM $MAINPID
ExecStopPost = /bin/rm -rf /run/gunicorn
PrivateTmp = true

Le processus de travail expire et redémarre constamment :

Jul 10 15:19:20 raspberryVM gunicorn[10941]: [2020-07-10 15:19:20 -0700] [10941] [CRITICAL] WORKER TIMEOUT (pid:10944)
Jul 10 15:19:20 raspberryVM gunicorn[10941]: [2020-07-10 15:19:20 -0700] [10944] [INFO] Worker exiting (pid: 10944)
Jul 10 15:20:15 raspberryVM gunicorn[10941]: [2020-07-10 15:20:15 -0700] [10985] [INFO] Booting worker with pid: 10985

app.py est une application Flask trival.

Ce problème est-il fermé en tant que Don't Fix ?

j'avais aussi le même problème

Mais après le débogage, j'ai pu constater que pendant que gunicorn démarre l'application Django, l'une des dépendances prenait plus de temps que le temps prévu (dans mon cas, une connexion DB externe), ce qui fait expirer le travailleur gunicron

Lorsque j'ai résolu le problème de connexion, le problème de délai d'attente a également été résolu ...

Ce ne serait pas mon cas. J'ai testé avec une application de type "Hello, World", sans dépendances. Cela me laisse donc toujours perplexe, mais il semble qu'il ne soit pas possible d'avoir Gunicorn avec un fil long. Le processus de travail redémarre et tue donc le thread de longue durée.

@leonbrag
Ce n'est probablement PAS un bug gunicorn. Voir mon commentaire ci-dessus dans le fil. C'est un effet secondaire des navigateurs qui envoient des connexions TCP "prévues" vides et exécutent gunicorn avec seulement quelques travailleurs de synchronisation sans protection contre les connexions TCP vides.

Existe-t-il une architecture/conception de référence qui montre un moyen approprié de configurer l'application Gunicorn flask avec un long thread de travail (permanent) ?

S'il ne s'agit pas d'un bogue, cela semble être un artefact ou une limitation de l'architecture/de la conception de Gunicorn.

Pourquoi le travailleur de synchronisation ne s'exécuterait-il pas pour toujours et n'accepterait-il pas les connexions des clients. Un tel travailleur fermerait le socket si nécessaire, tout en continuant à s'exécuter sans quitter (et par conséquent, le thread de travail continuerait à s'exécuter).

@leonbrag
Vous devriez être plus précis sur le problème que vous essayez de résoudre.

Le problème discuté dans ce fil se produit dans l'environnement de développement et la solution la plus simple consiste soit à ajouter plus de travailleurs de synchronisation, soit à utiliser des travailleurs à thread.

Si vous souhaitez éviter ce problème dans la configuration de la production, vous pouvez utiliser des travailleurs gevent ou vous pouvez placer un nginx devant gunicorn.
Certains PaaS placent déjà un nginx devant votre conteneur docker, vous n'avez donc pas à vous en soucier. Là encore, la solution dépend du contexte et des détails.

C'est une bonne lecture.
https://www.brianstorti.com/the-role-of-a-reverse-proxy-to-protect-your-application-against-slow-clients/

vous pouvez vérifier la page de conception de la documentation. Les travailleurs asynchrones en sont un
moyen d'exécuter de longues tâches.

Le samedi 8 août 2020 à 18h00, leonbrag [email protected] a écrit :

Existe-t-il une architecture/conception de référence qui montre une manière appropriée de configurer
Application de flacon Gunicorn avec un fil de travail long (permanent) ?

S'il ne s'agit pas d'un bogue, cela semble être un artefact ou une limitation du
Architecture/design Gunicorne.

Pourquoi le travailleur de synchronisation ne s'exécuterait-il pas pour toujours et n'accepterait-il pas les connexions des clients. Tel
worker fermerait le socket au besoin, tout en continuant à s'exécuter sans quitter
(et par conséquent, le thread de travail continue de s'exécuter).

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/1801#issuecomment-670944797 ,
ou se désinscrire
https://github.com/notifications/unsubscribe-auth/AAADRIWRQGIP3R5PMVJ5ENTR7VZA3ANCNFSM4FDLD5PA
.

>

Envoyé depuis mon mobile

web: gunicorn --workers=3 app:app --timeout 200 --log-file -

J'ai résolu mon problème en augmentant le --timeout

Voir aussi #1388 pour les problèmes de tmpfs liés à Docker.

Oh, merci beaucoup Randall, j'ai oublié d'ajouter --worker-tmp-dir /dev/shm aux arguments gunicorn lorsque j'exécutais gunicorn dans Docker.

D'ailleurs, 64 Mo suffiront-ils pour le cache gunicorn ?

app gunicorn
Ou
app gunicorn

A fonctionné pour moi... Je préfère un délai d'attente.

Bizarre, j'ai ajouté --worker-tmp-dir /dev/shm mais je reçois toujours :

[2020-11-27 21:01:42 +0000] [1] [CRITICAL] WORKER TIMEOUT (pid:17)

Pour m'assurer que /dev/shm est ramfs, je l'ai comparé :

image

Les paramètres sont les suivants :

    command: /bin/bash -c "cd /code/ && pipenv run gunicorn --worker-tmp-dir /dev/shm conf.wsgi:application --bind 0.0.0.0:8022 --workers 5 --worker-connections=1000"

PS : j'utilise PyPy

@attajutt timeout est agréable, mais vous risquez que le processus maître gunicorn ne détecte le raccrochage dans votre processus de travail qu'après 1000 secondes, et vous manquerez beaucoup de demandes. De plus, il sera difficile de le détecter si un seul des plusieurs travailleurs raccroche. Je ne ferais pas 1000 au moins.

@ivictbor merci pour lmk. 1000 est pour référence. Néanmoins, j'ai lancé l'application une fois qu'elle est chargée, elle fonctionne parfaitement.

J'ai eu ce problème d'erreur aussi et après plusieurs fois, j'ai trouvé que le problème est probablement causé :

  1. Configuration Nginx
  2. Gunicorne/Uwsgi

Si vous déployez votre application dans le cloud comme GAE, cela ne fera apparaître aucune erreur d'indication.
vous pouvez essayer de faire apparaître l'erreur en utilisant cette solution de cas : https://stackoverflow.com/questions/38012797/google-app-engine-502-bad-gateway-with-nodejs

Si soulevé 502 mauvaise passerelle ;
aura probablement 2 possibilités:

  1. gunicorn ne fonctionne pas
  2. gunicorn a un délai d'attente

sulotion complète expliquée ici : https://www.datadoghq.com/blog/nginx-502-bad-gateway-errors-gunicorn/

j'espère que cela peut réparer n'importe qui a eu une erreur dans [CRITICAL] WORKER TIMEOUT

Ajout d'une autre possibilité pour ceux qui trouvent ce fil...

Cela peut également être causé par des contraintes de ressources imposées par docker qui sont trop faibles pour votre application Web. Par exemple j'avais les contraintes suivantes :

services:
  web_app:
    image: blah-blah
    deploy:
      resources:
        limits:
          cpus: "0.25"
          memory: 128M

et ceux-ci étaient évidemment trop bas pour gunicorn , j'ai donc constamment eu l'erreur [CRITICAL] WORKER TIMEOUT jusqu'à ce que je supprime les contraintes.

Pour gunicorn, ces ressources conviennent parfaitement. Mais il faut en effet
avion pour le nombre de travailleurs et les ressources nécessaires à votre
application. 128M et 0.25cpu semble vraiment bas pour une application web
écrit en Python... en règle générale, vous avez besoin d'au moins 1 cœur /vcpu et
512 Mo de RAM au strict minimum.

Le ven 26 mars 2021 à 02:14, Colton Hicks @ . * > a écrit :

Ajout d'une autre possibilité pour ceux qui trouvent ce fil...

Cela peut également être dû au fait que docker a imposé des contraintes de ressources qui
sont trop bas pour votre application Web. Par exemple j'ai eu ce qui suit
contraintes:

prestations de service:
application_web :
image : bla-bla
déployer:
Ressources:
limites:
processeur : "0.25"
mémoire : 128 M

et ceux-ci étaient évidemment trop bas pour le gunicorn, donc j'ai constamment le [CRITIQUE]
Erreur WORKER TIMEOUT jusqu'à ce que j'aie supprimé les contraintes.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/1801#issuecomment-807855647 ,
ou se désinscrire
https://github.com/notifications/unsubscribe-auth/AAADRITPZB7BMA6QW7LFNVLTFPNV3ANCNFSM4FDLD5PA
.

>

Envoyé depuis mon mobile

--timeout=1000 a travaillé pour moi. Le problème était une machine GCP disposant de peu de ressources en processeur. Cela a bien fonctionné sur ma machine locale avec le délai d'attente par défaut.

Cette page vous a été utile?
0 / 5 - 0 notes