<p>gunicorn 19.x вызывает проблемы с django-1.7.1 и gevent</p>

Созданный на 4 нояб. 2014  ·  53Комментарии  ·  Источник: benoitc/gunicorn

Привет,

Я вижу эту ошибку с Gunicorn 19.x (проверено с 19.0 и 19.1.1 ) с использованием рабочего gevent . Я использую django-1.7.1 .

это исключение:

Traceback:
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  87.                 response = middleware_method(request)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/middleware.py" in process_request
  34.         if user and hasattr(user, 'get_session_auth_hash'):
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/utils/functional.py" in inner
  224.             self._setup()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/utils/functional.py" in _setup
  357.         self._wrapped = self._setupfunc()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/middleware.py" in <lambda>
  23.         request.user = SimpleLazyObject(lambda: get_user(request))
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/middleware.py" in get_user
  11.         request._cached_user = auth.get_user(request)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/__init__.py" in get_user
  151.         user_id = request.session[SESSION_KEY]
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py" in __getitem__
  49.         return self._session[key]
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py" in _get_session
  175.                 self._session_cache = self.load()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/sessions/backends/db.py" in load
  21.                 expire_date__gt=timezone.now()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/manager.py" in manager_method
  92.                 return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in get
  351.         num = len(clone)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in __len__
  122.         self._fetch_all()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in _fetch_all
  966.             self._result_cache = list(self.iterator())
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in iterator
  265.         for row in compiler.results_iter():
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in results_iter
  700.         for rows in self.execute_sql(MULTI):
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  784.         cursor = self.connection.cursor()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/backends/__init__.py" in cursor
  163.         self.validate_thread_sharing()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/backends/__init__.py" in validate_thread_sharing
  515.                 % (self.alias, self._thread_ident, thread.get_ident()))

Exception Type: DatabaseError at /
Exception Value: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 140049505195808 and this is thread id 61130224.

Вот мой gunicorn.conf.py :

##
# Gunicorn config at 
# Managed by Chef - Local Changes will be Nuked from Orbit (just to be sure)
##

# What ports/sockets to listen on, and what options for them.
bind = "127.0.0.1:9300"

# The maximum number of pending connections
backlog = 2048

# What the timeout for killing busy workers is, in seconds
timeout = 300

# How long to wait for requests on a Keep-Alive connection, in seconds
keepalive = 2

# The maxium number of requests a worker will process before restarting
max_requests = 1024

# Whether the app should be pre-loaded
preload_app = True

# How many worker processes
workers = 16

# Type of worker to use
worker_class = "gevent"

# The granularity of error log outputs.
loglevel = "error"

Ошибка возникает каждый раз, простой доступ к любому URL-адресу приложения django (который, конечно, зависит от любой модели) вызывает эту ошибку. Любые идеи?
Поскольку переход на gunicorn-18.0 решает проблему, я предполагаю, что gunicorn-19.x делает что-то другое.

Если вам нужна дополнительная информация, дайте мне знать, и я размещу ее здесь.

На данный момент я использую 18.0 .

Большое спасибо.

( FeaturWorker FeaturCore ThirdpartGevent Deploy Investigation - Bugs -

Самый полезный комментарий

Предлагаемое @gukoff исправление не сработало для меня, я все еще получаю ту же ошибку 😞

Все 53 Комментарий

Сначала просто вопрос: помогает ли отключение опции предварительной нагрузки?
Думаю, я помню, как сообщалось об еще одной подобной проблеме.
Если изменение этой опции помогает, значит, мы должны что-то изменить в способе загрузки приложений Django, и нам будет легче отследить это.

Я _ думаю_, что мы импортируем приложение django wsgi в главный процесс, который, по крайней мере, на django 1.7, инициализирует все модели django при импорте.

Привет, @tilgovi , спасибо за preload_app = False я могу использовать gunicorn-19.1.1 и django-1.7.1 .

Вы хоть представляете, что облако-пулеметчик по-другому делает с этим в версиях 19.x ?

У меня теперь вторая проблема. Я использую Raven [1] в своем приложении django. И согласно документации Raven, при использовании промежуточного программного обеспечения WSGU [1] можно использовать Raven как объект WSGI (обертывание исходного приложения django WSGI), а при использовании gunicorn (как внешняя команда, а не run_gunicorn django command) мы должны добавить ловушку к gunicorn [2]. Когда этот перехватчик включен, gunicorn загружается, только если preload_app = True , но тогда gevent не работает. Когда я использую preload_app = True с включенным перехватчиком, gunicorn даже не загружается, что дает мне и ImportError говоря, что он не может импортировать мой "DJANGO_SETTINGS_MODULE".

Ворон - 4.2.3

А пока я вернусь к gunicorn 18.x , поскольку он работает с обеими конфигурациями (gevent и raven как промежуточное ПО WSGI).
Следует ли мне открыть еще один вопрос по этому поводу?

[1] http://raven.readthedocs.org/en/latest/config/django.html#wsgi -middleware
[2] http://raven.readthedocs.org/en/latest/config/django.html#gunicorn

@daltonmatos способ настройки баз данных в django 1.7.1, вероятно, изменился, по-видимому, не является потокобезопасным. Я не знаю, что делать в этот момент.

Еще одна вещь, которую я узнал сегодня,
обработка сеанса в django 1.7 с Gunicorn 19.3.0 истекает через секунды.

Пришлось вернуться к 18, чтобы все заработало.

К вашему сведению.

Похоже, это та же проблема, что и https://github.com/benoitc/gunicorn/issues/879 , который был закрыт, но не исправлен. Это делает Gunicorn 19.x. невозможно использовать с Django. Возможно, только для работника gevent? Я не тестировал другие типы рабочих.

@tilgovi Интересно, не связано ли это с изменениями, wsgi.multithread в true где это было false в 18.x:

https://github.com/benoitc/gunicorn/blob/master/gunicorn/workers/ggevent.py#L99

Это изменение может обрабатываться в django по-другому. А гринлет - это не совсем нить. Мысли? По крайней мере, возможно, мы могли бы попытаться вернуть его и проверить, решает ли он проблему. Есть ли у нас тест или воспроизводимый способ решения этой проблемы?

О, я уверен, что проблема в этом. Доброй памяти, @benoitc.

Я до сих пор не знаю почему. Как я сказал в то время, чтобы оправдать это изменение, я не могу себе представить, что это приведет к поломке чего-либо, потому что это будет означать, что фреймворк будет принимать только дополнительные меры предосторожности при совместном использовании данных. Видимо, могу ошибаться.

Я нажал ветку fix/927 чтобы проверить, работает ли вышеуказанное исправление. Пожалуйста, протестируйте!

Я все еще могу воспроизвести эту ошибку, используя fix / 927 с django 1.8.1 (и 1.7.1) и gevent 1.0.1

Это меня обнадеживает, даже если мы до сих пор не знаем причины.

Поэтому я попытался использовать пример приложения и не воспроизвел проблему, выполнив следующую командную строку:

gunicorn --env DJANGO_SETTINGS_MODULE=testing.settings -k gevent -w3 wsgi:application

Может ли кто-нибудь дать мне воспроизводимый пример?

Я все еще могу воспроизвести эту проблему, вызывая Gunicorn следующим образом:

    gunicorn --env DJANGO_SETTINGS_MODULE=settings -w 3 --max-requests=1000 -b 0.0.0.0:8000 -k gevent --config ogs/gunicorn.py  --pythonpath ogs wsgi:application

Есть ли какие-нибудь подробности, которые вам нужны?

@brockhaywood должен быть project.settings , но да, мне нужен способ воспроизвести его, есть ли у вас какой-нибудь минимальный код, которым можно поделиться.

Глядя на след выше, это также может быть исправление обезьяны. Может быть, темы не следует исправлять обезьянами. Можете попробовать следующий патч?

--- a/gunicorn/workers/ggevent.py
+++ b/gunicorn/workers/ggevent.py
@@ -60,9 +60,9 @@ class GeventWorker(AsyncWorker):

         # if the new version is used make sure to patch subprocess
         if gevent.version_info[0] == 0:
-            monkey.patch_all()
+            monkey.patch_all(thread=False)
         else:
-            monkey.patch_all(subprocess=True)
+            monkey.patch_all(subprocess=True, thread=False)

         # monkey patch sendfile to make it none blocking
         patch_sendfile()

Использование вышеуказанного патча со следующим wsgi больше не вызывает указанную ошибку:

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from gevent import monkey
monkey.patch_all(thread=False)

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

from django.core.cache.backends.memcached import BaseMemcachedCache
BaseMemcachedCache.close = lambda self, **kwargs: None

Теперь я вижу другую и новую ошибку, но не знаю, связана ли она:

ProgrammingError: close cannot be used while an asynchronous query is underway

Это происходит, 1 из 10/15 запросов выполняет подсчет модели. Я подозреваю, что это ошибка моего приложения, которая теперь обнаружена.

@brockhaywood круто! кстати а какая у тебя версия gevent? Для вашей последней ошибки он умирает, действительно, похоже, не связан с пулеметом.

Ага, хорошие новости! В версии 1.0.1 gevent.

@tilgovi, поэтому мне интересно, не следует ли нам патчить Gunicorn, удаляя исправление thread monkey. Может быть, нам также следует установить wsgi.multithread на False ? Мысли?

@benoitc Я могу воспроизвести новую ошибку, на которую я ссылался выше, с помощью очень простого приложения Django, которое имеет только одно представление и выполняет простой модельный запрос и подсчитывает, если я выполняю несколько одновременных запросов.

Есть ли у вас какие-либо предложения относительно того, какой компонент является наиболее вероятным виновником? Вероятно, это проблема gevent или психоэкран?

Скорее психогрин. Используете ли вы такой хук для его настройки?

def def_post_fork(server, worker):
    from psycogreen.gevent import psyco_gevent
    psyco_gevent.make_psycopg_green()
    worker.log.info("Made Psycopg Green")

О, я на самом деле использую

def post_fork(server, worker):    
    from psycogreen.gevent import patch_psycopg
    patch_psycopg()

Я попробую ваше предложение.

Ваш код будет более точным, если я прочитаю код psycogreen:

https://bitbucket.org/dvarrazzo/psycogreen/src/115d0627da1ac9ff48c0cb9287257cd35868cdf9/psycogreen/gevent.py?at=master#cl -17

Хорошо, попробую открыть билет с помощью Psycogreen

можно попробовать использовать ловушку post_worker_init вместо post_fork ?

Все еще возникает у меня при патче psycogreen в post_worker_init.

Я также отправил заявку на Psycogreen:
https://bitbucket.org/dvarrazzo/psycogreen/issue/2/databaseerror-used-with-asynchronous-query

@brockhaywood OK: / Также нашел эту ссылку: https://bitbucket.org/dvarrazzo/psycogreen-hg/issue/1/databaseerror-execute-used-with . Я не уверен, может ли это вам помочь.

Я посмотрел на это, и это похоже на ту же проблему, но, к сожалению, я не нашел решения.

@brockhaywood, не могли бы вы поделиться каким-нибудь простым примером кода, который помог бы мне воспроизвести проблему? Я посмотрю на это завтра.

Вот простой проект, который я использую для воспроизведения:
https://github.com/brockhaywood/gunicorn_gevent_issue

Благодарность!

Вск, 17 мая 2015 г., 22:34. Brockhaywood [email protected]
написал:

Вот простой проект, который я использую для воспроизведения:
https://github.com/brockhaywood/gunicorn_gevent_issue

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -102852497.

@benoitc есть мысли по этому

@brockhaywood, извини, у меня не было времени проверить это. Поскольку завтра меня посадят в рейс, я все равно буду :)

Спасибо
26 мая 2015 года в 13:17 «Бенуа Шено» [email protected] написал:

@brockhaywood https://github.com/brockhaywood извините, у меня не было времени
чтобы проверить это. Поскольку завтра меня посадят в рейс, я все равно буду :)

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -105652947.

Когда я увидел эту ветку, у меня возникла та же проблема с DatabaseWrapper.

Я могу подтвердить, что удаление параметра preload_app из моего файла конфигурации Gunicorn решает проблему. Я использую:

  • Django == 1.7.7
  • gevent == 1.0.2
  • пулеметчик == 19.3.0
  • psycopg2 == 2.5.2
  • psycogreen == 1.0
  • Gunicorn, завернутый в PgBouncer

(прыгает, так как мы наблюдаем те же проблемы, что описаны в этой ветке)

Похоже, эти проблемы вызваны исправлением проблемы № 478 (не пытайтесь исправлять мастер).

Поскольку не обезьяна исправляет мастер, предварительная загрузка приложения в корне создает несоответствие, если вы используете gevent worker. Любой код приложения, который запускается как часть предварительной загрузки, запускается в среде, не исправленной обезьяной. Любой код приложения, который выполняется в рабочем, выполняется в среде, пропатченной обезьяной.

Для нас, просто восстановив метод setup для рабочего класса gevent как он определен в 18.x, чтобы мастер был исправлен как обезьяна, и он был исправлен до предварительной загрузки приложения, исправлено все наши проблемы. Это включает ошибку DatabaseWrapper , а также другую проблему, которую мы наблюдали, когда рабочие gevent зависали и превышали время ожидания. Никаких других изменений не потребовалось (например, нам не нужно отключать исправление обезьяны threads ).

Я все еще очень не решаюсь исправлять главный процесс как обезьяны, но если вы хотите это сделать, вы можете использовать серверные хуки вместо исправления самого Gunicorn. Если вы можете это сделать, я был бы рад, если мы сможем задокументировать это где-нибудь лучше, чем здесь.

Я думаю, что ближайший план для нас - создать (внутренний) форк Gunicorn с применением этого патча.

Я исследую перенос шага патча обезьяны в наш код wsgi, но это все еще кажется чем-то, что должно быть сделано внутри кода gunicorn (даже в качестве настраиваемой опции). Исправление в предварительно загруженном коде приложения так или иначе повлияет на мастер, и весь смысл будет заключаться в том, чтобы сохранить исправленное состояние рабочих процессов обезьяной в синхронизации с мастером. Применение патча master monkey в коде приложения означает, что разработчики приложений, которые зависят от этого поведения, всегда будут догонять любые изменения, которые Gunicorn вносит в то, когда и как обезьяна исправляет рабочего.

Если раннее исправление сработает, возможно, это следует рассматривать как изменение.

Я не могу вспомнить, было ли это когда-либо так и вызывали ли это проблемы, но с патчем обезьяны всегда сложно.

Наше собственное производственное приложение Gunicorn требует предварительной загрузки, а также требует исправления обезьяны в мастере (мы используем сценарий Pyramid / paste pserve для запуска приложения и загрузки Gunicorn из файла ini; у нас есть оболочка вокруг него, чтобы быть уверен, что исправление обезьяны произойдет как можно скорее).

Я текущий сопровождающий gevent. Я буду рад рассмотреть любые проблемы в gevent, необходимые для улучшения работы этого сценария. Мы ожидаем, что скоро выпустим 1.1, и было бы здорово, если бы этот вариант поддерживался.

Я попытался применить ту же логику патча обезьяны в верхней части нашей реализации wsgi, и мы начали замечать проблему DatabaseWrapper . Не совсем уверен, почему - если посмотреть на вызов wsgi в GeventWorker.setup (как побочный эффект присваивания worker_class . Если я читаю код правильный.

Я буду следить, если у меня возникнет эврика или мне повезет с кодом приложения, но пока для нас исправление ранее в мастер-коде Gunicorn - единственное исправление, которое решает все наши проблемы.

@jamadden большое спасибо.

@jzupko обычно важно, чтобы исправление происходило до любого импорта, который может быть затронут. Вот почему я предложил сделать это с помощью сервера, например, поместив функцию post_fork в файл gunicorn.conf.py . Однако я только что понял, что предварительная загрузка приложения происходит до любого из перехватов сервера, и неясно, где это сделать.

@benoitc есть ли у нас какой-либо механизм для конфигурирования, специфичного для рабочего?

Я добавляю это к этапу выпуска 20 и предлагаю подумать о том, чтобы обезьяна внесла исправления в мастер, возможно, если это можно будет сделать без повторного введения таких проблем, как # 478.

@tilgovi у вас есть метод setup рабочего, который, я думаю, можно использовать для этого.

Есть ли у кого-нибудь временное исправление, пока не выйдет 20.0? Или это единственный выход: отключить preload_app или понизить до 18.0?

@fletom временное исправление для? Вы пробовали последний мастер? Это сработало для вас?

@benoitc Я имею в виду исправление ошибки "Объекты DatabaseWrapper".

Текущий мастер-пулемет вообще не работает для меня. На обычном 19.6.0 я получаю это (нормально):

[2017-01-05 15:46:32 -0500] [3222] [INFO] Starting gunicorn 19.6.0
[2017-01-05 15:46:32 -0500] [3222] [INFO] Listening at: http://127.0.0.1:8000 (3222)
[2017-01-05 15:46:32 -0500] [3222] [INFO] Using worker: gevent
[2017-01-05 15:46:32 -0500] [3226] [INFO] Booting worker with pid: 3226
[2017-01-05 15:46:32 -0500] [3227] [INFO] Booting worker with pid: 3227
[2017-01-05 15:46:32 -0500] [3228] [INFO] Booting worker with pid: 3228
[2017-01-05 15:46:32 -0500] [3229] [INFO] Booting worker with pid: 3229

И на последнем мастере со 100% точно такой же конфигурацией я получаю это, и, насколько я могу судить, ничего не прослушивает ни один порт:

[2017-01-05 15:47:19 -0500] [3308] [INFO] Starting gunicorn 19.6.0
[2017-01-05 15:47:19 -0500] [3308] [INFO] Listening at:  (3308)
[2017-01-05 15:47:19 -0500] [3308] [INFO] Using worker: gevent
[2017-01-05 15:47:19 -0500] [3312] [INFO] Booting worker with pid: 3312
[2017-01-05 15:47:19 -0500] [3313] [INFO] Booting worker with pid: 3313
[2017-01-05 15:47:19 -0500] [3314] [INFO] Booting worker with pid: 3314
[2017-01-05 15:47:19 -0500] [3315] [INFO] Booting worker with pid: 3315

Кажется, прослушивается?

@fletom как запустить пулемет?

@benoitc В данном случае это gunicorn -c gunicorn_conf.py <appname>.wsgi .

Мой gunicorn_conf.py :

workers = 4

worker_class = 'gevent'

preload_app = False

Я натыкаюсь на эту проблему с gunicorn==18.0 . В gunicorn_conf.py у меня есть worker_class = 'gevent' , preload_app = True и пользовательские функции pre_fork , on_starting и on_reload .

Размещение этих строк поверх файла gunicorn_conf.py, похоже, решает проблему для меня:

import gevent.monkey
gevent.monkey.patch_thread()

Обновлять:
У меня были эти строки в моем gunicorn.conf. Избавление от них решило проблему без исправления обезьяны.

import django
django.setup()

Из-за них django инициировал соединение до того, как Gunicorn произвел исправление обезьяны.

Предлагаемое @gukoff исправление не сработало для меня, я все еще получаю ту же ошибку 😞

Я собираюсь закрыть это и начать обсуждение с лейблом списка рассылки о вехе R20 для обезьяньего исправления арбитра в gevent. Если у вас возникли проблемы, связанные с чем-либо в этом обсуждении, и вы думаете, что это может быть ошибка в Gunicorn, пожалуйста, откройте проблему. Вы можете упомянуть его по номеру или ссылку на комментарии, если это поможет добавить контекст, но на данный момент это старый билет с параллельными разговорами о нескольких версиях Gunicorn и различных симптомах.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

Abraxos picture Abraxos  ·  4Комментарии

gr790 picture gr790  ·  4Комментарии

ttcqaq picture ttcqaq  ·  4Комментарии

zenglingyu picture zenglingyu  ·  4Комментарии

benoitc picture benoitc  ·  4Комментарии