์๋ ํ์ญ๋๊น,
gevent
์์
์๋ฅผ ์ฌ์ฉํ์ฌ gunicorn 19.x
( 19.0
๋ฐ 19.1.1
ํ
์คํธ)์์ ์ด ์ค๋ฅ๊ฐ ํ์๋ฉ๋๋ค. 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"
์ค๋ฅ๋ ๋งค๋ฒ ๋ฐ์ํ๋ฉฐ django ์ฑ์ URL(๋ฌผ๋ก ๋ชจ๋ธ์ ๋ฐ๋ผ ๋ค๋ฆ)์ ์ก์ธ์คํ๋ฉด ์ด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด๋ค ์์ด๋์ด?
gunicorn-18.0
๋ณ๊ฒฝํ๋ฉด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋๋ฏ๋ก gunicorn-19.x
๊ฐ ๋ค๋ฅธ ์์
์ ์ํํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
์ถ๊ฐ ์ ๋ณด๊ฐ ํ์ํ ๊ฒฝ์ฐ ์๋ ค์ฃผ์๋ฉด ์ฌ๊ธฐ์ ๊ฒ์ํ๊ฒ ์ต๋๋ค.
์ง๊ธ์ 18.0
์์ต๋๋ค.
์ ๋ง ๊ฐ์ฌํฉ๋๋ค.
๋จผ์ ์ง๋ฌธ๋ง ํ์ญ์์ค. ์ฌ์ ๋ก๋ ์ต์
์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋์์ด ๋ฉ๋๊น?
๋ค๋ฅธ ์ ์ฌํ ๋ฌธ์ ๊ฐ ๋ณด๊ณ ๋ ๊ฒ์ผ๋ก ๊ธฐ์ตํฉ๋๋ค.
ํด๋น ์ต์
์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ๋์์ด ๋๋ค๋ฉด Django ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ก๋๋๋ ๋ฐฉ์์ ๋ณ๊ฒฝํด์ผ ํ๊ณ ์ถ์ ํ๊ธฐ๊ฐ ๋ ์ฌ์ธ ๊ฒ์
๋๋ค.
๋๋ ์ ์ด๋ django 1.7์์๋ ๊ฐ์ ธ์ฌ ๋ ๋ชจ๋ django ๋ชจ๋ธ์ ์ด๊ธฐํํ๋ ๋ง์คํฐ ํ๋ก์ธ์ค์์ django wsgi ์ ํ๋ฆฌ์ผ์ด์ ์ _์๊ฐ_ํฉ๋๋ค.
์๋
ํ์ธ์ @tilgovi , ์๊ฐ ๋ด์ฃผ์
์ ๊ฐ์ฌํฉ๋๋ค. preload_app = False
๋ฅผ ์ฌ์ฉํ์ฌ gunicorn-19.1.1
๋ฐ django-1.7.1
๋ฅผ ์ฌ์ฉํ ์ ์๋์ง ํ์ธํ ์ ์์ต๋๋ค.
19.x
๋ฒ์ ์์ ์ด์ ๋ํด gunicorn ํด๋ผ์ฐ๋๊ฐ ์ด๋ป๊ฒ ๋ค๋ฅธ์ง ์๊ณ ์์ต๋๊น?
์ด์ ๋ ๋ฒ์งธ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ๋ด django ์ฑ์์ Raven[1]์ ์ฌ์ฉํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ Raven ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด WSGU ๋ฏธ๋ค์จ์ด[1]๋ฅผ ์ฌ์ฉํ ๋ Raven์ WSGI ๊ฐ์ฒด๋ก ์ฌ์ฉํ ์ ์๊ณ (์๋ django WSGI ์ฑ ๋ํ) gunicorn์ ์ฌ์ฉํ ๋( run_gunicorn
์๋ ์ธ๋ถ ๋ช
๋ น์ผ๋ก) preload_app = True
์ธ ๊ฒฝ์ฐ์๋ง ๋ถํ
๋์ง๋ง gevent๋ ์๋ํ์ง ์์ต๋๋ค. ๋ด๊ฐ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ preload_app = True
ํํฌ ํ์ฑํ์ ํจ๊ป, gunicorn๋ ๋ถํ
, ์ ๋ฅผ ํฌ๊ธฐํ์ง ์๊ณ ImportError
๋ด "DJANGO_SETTINGS_MODULE"์ ๊ฐ์ ธ์ฌ ์ ์๋ค๋.
๊น๋ง๊ท๋ 4.2.3
์ง๊ธ์ gunicorn 18.x
๋์๊ฐ๊ฒ ์ต๋๋ค. ๋ ๊ตฌ์ฑ(WSGI ๋ฏธ๋ค์จ์ด๋ก gevent ๋ฐ raven)์์ ์๋ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
์ด์ ๋ํด ๋ค๋ฅธ ๋ฌธ์ ๋ฅผ ์ด์ด์ผ ํฉ๋๊น?
[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์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ค์ ๋๋ ๋ฐฉ์์ ์๋ง๋ ์ค๋ ๋๋ก๋ถํฐ ์์ ํ์ง ์์ ๊ฒ์ผ๋ก ๋ณ๊ฒฝ๋์์ ๊ฒ์ ๋๋ค. ๊ทธ ์์ ์์ ๋ฌด์์ ํด์ผ ํ ์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
๋ ํ๋ ์ค๋ ์๊ฒ ๋ ์ฌ์ค,
gunicorn 19.3.0์ด ํฌํจ๋ django 1.7์ ์ธ์
์ฒ๋ฆฌ๊ฐ ๋ช ์ด ์์ ๋ง๋ฃ๋ฉ๋๋ค.
์๋ํ๋ ค๋ฉด 18๋ก ๋ค์ ์ ํํด์ผ ํ์ต๋๋ค.
์ฐธ๊ณ ๋ก
์ด๊ฒ์ ํ์๋์์ง๋ง ์์ ๋์ง ์์ https://github.com/benoitc/gunicorn/issues/879 ์ ๋์ผํ ๋ฌธ์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๊ฒ์ gunicorn 19.x๋ฅผ ๋ง๋ญ๋๋ค. Django์์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ง๋ gevent ์์ ์์๊ฒ๋ง ํด๋น๋ฉ๋๊น? ๋ค๋ฅธ ์์ ์ ์ ํ์ ๋ํด์๋ ํ ์คํธํ์ง ์์์ต๋๋ค.
@tilgovi ํ๊ฒฝ์์ ์ํ๋ ๋ณ๊ฒฝ์ผ๋ก ์ธํ ๊ฒ์ด wsgi.multithread
๋ฅผ true
๋ก ์ค์ ํ์ผ๋ฉฐ 18.x์์๋ false
์์ต๋๋ค.
https://github.com/benoitc/gunicorn/blob/master/gunicorn/workers/ggevent.py#L99
์ด ๋ณ๊ฒฝ์ django์์ ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌ๋ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ greenlet์ ์ค์ ๋ก ์ค๋ ๋๊ฐ ์๋๋๋ค. ์๊ฐ? ์ ์ด๋ ๋๋๋ฆฌ๊ณ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋๋์ง ํ์ธํ ์๋ ์์ต๋๋ค. ์ด ๋ฌธ์ ์ ๋ํ ํ ์คํธ ๋๋ ์ฌํ ๊ฐ๋ฅํ ๋ฐฉ๋ฒ์ด ์์ต๋๊น?
์, ๊ทธ๊ฒ ๋ฌธ์ ์ธ ๊ฒ ๊ฐ์์. ์ข์ ๊ธฐ์ต, @benoitc.
์์ง๊น์ง๋ ์ด์ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ๋ณ๊ฒฝ์ ์ ๋นํํ๊ธฐ ์ํด ๋น์์ ๋งํ๋ฏ์ด ํ๋ ์์ํฌ๊ฐ ๋ฐ์ดํฐ ๊ณต์ ์ ๋ํด ๋ ๋ง์ ์๋ฐฉ ์กฐ์น๋ฅผ ์ทจํ๋ค๋ ๊ฒ์ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ ์๋ฌด ๊ฒ๋ ์ค๋จํ ๊ฒ์ด๋ผ๊ณ ์์ํ ์ ์์ต๋๋ค. ๋ถ๋ช ํ, ๋ด๊ฐ ํ๋ฆด ์ ์์ต๋๋ค.
์์ ์์ ์ฌํญ์ด ์๋ํ๋์ง ํ์ธํ๊ธฐ ์ํด ๋ถ๊ธฐ fix/927
๋ฅผ ํธ์ํ์ต๋๋ค. ํ
์คํธ ํด์ฃผ์ธ์!
๋๋ ์ฌ์ ํ django 1.8.1(๋ฐ 1.7.1) ๋ฐ gevent 1.0.1์์ fix/927์ ์ฌ์ฉํ์ฌ ์ด ์ค๋ฅ๋ฅผ ์ฌํํ ์ ์์ต๋๋ค.
์์ง ์์ธ์ ์์ง ๋ชปํ๋๋ผ๋ ์์ฌ์ด ๋ฉ๋๋ค.
๊ทธ๋์ ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉํด ๋ณด์์ง๋ง ๋ค์ ๋ช ๋ น์ค์ ์คํํ๋ ๋ฌธ์ ๋ฅผ ์ฌํํ์ง ๋ชปํ์ต๋๋ค.
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
์ด๋ 10/15 ์์ฒญ ์ค 1๊ฑด์ด ๋ชจ๋ธ์์ ๊ณ์ฐ์ ์ํํ๋ ์ค์ ๋๋ค. ๋๋ ์ด๊ฒ์ด ์ง๊ธ ๋ฐํ์ง ๋ด ์์ฉ ํ๋ก๊ทธ๋จ์ ์ค๋ฅ๋ผ๊ณ ์๊ฐํฉ๋๋ค.
@brockhaywood ๋ฉ์ง๋ค! btw ahat ๋น์ ์ gevent ๋ฒ์ ์ ๋๊น? ์ต์ ์ค๋ฅ์ ๊ฒฝ์ฐ ์ค์ ๋ก gunicorn๊ณผ ๊ด๋ จ์ด ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ข์ ์์์ ๋๋ค! gevent ๋ฒ์ 1.0.1์์.
@tilgovi ๊ทธ๋์ ์ค๋ ๋ ์์ญ์ด ํจ์น๋ฅผ ์ ๊ฑฐํ๋ gunicorn์ ํจ์นํด์๋ ์๋๋์ง ๊ถ๊ธํฉ๋๋ค. wsgi.multithread
๋ False
ํด์ผ ํ ๊น์? ์๊ฐ?
@benoitc ๋ณด๊ธฐ๊ฐ ํ๋๋ง ์๊ณ ์ฌ๋ฌ ๋์ ์์ฒญ์ ์คํํ๋ ๊ฒฝ์ฐ ๊ฐ๋จํ ๋ชจ๋ธ ์ฟผ๋ฆฌ ๋ฐ ๊ณ์ฐ์ ์ํํ๋ ๋งค์ฐ ๊ฐ๋จํ Django ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ์์์ ์ฐธ์กฐํ ์๋ก์ด ์ค๋ฅ๋ฅผ ์ฌํํ ์ ์์ต๋๋ค.
๊ฐ์ฅ ๊ฐ๋ฅ์ฑ์ด ๋์ ๊ตฌ์ฑ ์์์ ๋ํ ์ ์ ์ฌํญ์ด ์์ต๋๊น? ์ด๊ฒ์ gevent ๋ฌธ์ ๋๋ psycogreen ๊ฐ๋ฅ์ฑ์ด ์์ต๋๊น?
์ฌ์ด์ฝ๊ทธ๋ฆฐ์ผ ๊ฐ๋ฅ์ฑ์ด ๋ ํฝ๋๋ค. ๊ทธ๋ฐ ํํฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ ํฉ๋๊น?
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์ ์ฝ๋๋ฅผ ์ฝ์ผ๋ฉด ์ฝ๋๊ฐ ๋ ์ ํํฉ๋๋ค.
์ข์, ์ธ์ด์ฝ๊ทธ๋ฆฐ์ผ๋ก ํฐ์ผ์ ์ด์ด๋ณผ๊ฒ
๋น์ ์ ํํฌ๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ ์ ์์ต๋๋ค post_worker_init
๋์ post_fork
?
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
๊ฐ์ฌ ํด์!
2015๋
5์ 17์ผ ์ผ์์ผ ์คํ 10:34 brockhaywood [email protected]
์ผ๋ค:
๋ค์์ ์ฌํํ๋ ๋ฐ ์ฌ์ฉํ๋ ๊ฐ๋จํ ํ๋ก์ ํธ์ ๋๋ค.
https://github.com/brockhaywood/gunicorn_gevent_issueโ
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ฑฐ๋ GitHub์์ ํ์ธํ์ธ์.
https://github.com/benoitc/gunicorn/issues/927#issuecomment -102852497.
@benoitc ์ด์ ๋ํ ์๊ฐ์ด ์์ต๋๊น?
@brockhaywood ํ ์คํธํ ์๊ฐ์ด
๊ฐ์ฌ ํด์
2015๋
5์ 26์ผ ์คํ 1์ 17๋ถ์ "Benoit Chesneau" [email protected]์ ๋ค์๊ณผ ๊ฐ์ด ์ผ์ต๋๋ค.
@brockhaywood https://github.com/brockhaywood ์๊ฐ์ด ์์ด์ ์ฃ์กํฉ๋๋ค
๊ทธ๊ฒ์ ํ ์คํธํฉ๋๋ค. ๋ด์ผ ๋นํ๊ธฐ์ ๋ฌถ์ฌ ์๊ธฐ ๋๋ฌธ์ ์ด์จ๋ ๊ฐ ๊ฒ์ ๋๋ค :)โ
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ฑฐ๋ GitHub์์ ํ์ธํ์ธ์.
https://github.com/benoitc/gunicorn/issues/927#issuecomment -105652947.
์ด ์ค๋ ๋๋ฅผ ๋ณด์์ ๋ ๋์ผํ DatabaseWrapper ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
๋ด gunicorn ๊ตฌ์ฑ ํ์ผ์์ preload_app
์ต์
์ ์ ๊ฑฐํ๋ฉด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋๋ค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ๋๋ ์ฌ์ฉํ๊ณ ์๋ค :
(์ด ์ค๋ ๋์์ ์ค๋ช ํ ๊ฒ๊ณผ ๋์ผํ ๋ฌธ์ ๋ฅผ ๋ณด๊ณ ์์๊ธฐ ๋๋ฌธ์ ๋ฐ์ด๋ค์์ต๋๋ค.)
์ด๋ฌํ ๋ฌธ์ ๋ ๋ฌธ์ #478 (์์ญ์ด ํจ์น๋ฅผ ๋ง์คํฐํ์ง ์์)์ ๋ํ ์์ ์ผ๋ก ์ธํด ๋ฐ์ํ ๊ฒ ๊ฐ์ต๋๋ค.
์์ญ์ด๊ฐ ๋ง์คํฐ๋ฅผ ํจ์นํ์ง ์์์ผ๋ก์จ gevent ์์ ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ฑ ์ฌ์ ๋ก๋๋ ๊ทผ๋ณธ์ ์ผ๋ก ๋ถ์ผ์น๋ฅผ ์์ฑํฉ๋๋ค. ์ฌ์ ๋ก๋์ ์ผ๋ถ๋ก ์คํ๋๋ ๋ชจ๋ ์ฑ ์ฝ๋๋ ์์ญ์ด ํจ์น๊ฐ ์ ์ฉ๋์ง ์์ ํ๊ฒฝ์์ ์คํ๋ฉ๋๋ค. ์์ ์์์ ์คํ๋๋ ๋ชจ๋ ์ฑ ์ฝ๋๋ ์์ญ์ด ํจ์น ํ๊ฒฝ์์ ์คํ๋ฉ๋๋ค.
์ฐ๋ฆฌ์ ๊ฒฝ์ฐ setup
๋ฉ์๋๋ฅผ gevent
์์
์ ํด๋์ค๋ก ๋ณต์ํ๊ธฐ setup
ํ๋ฉด 18.x์ ์ ์๋ ๋๋ก ๋ง์คํฐ๊ฐ ์์ญ์ด ํจ์น๋๊ณ ์ฑ ์ฌ์ ๋ก๋ ์ด์ ์ ์์ญ์ด ํจ์น๋ฉ๋๋ค. ์ฐ๋ฆฌ์ ๋ฌธ์ . ์ฌ๊ธฐ์๋ DatabaseWrapper
๋ฒ๊ทธ์ gevent ์์
์๊ฐ ๋ฉ์ถ๊ณ ์๊ฐ ์ด๊ณผ๋๋ ๋ ๋ค๋ฅธ ๋ฌธ์ ๊ฐ ํฌํจ๋ฉ๋๋ค. ๋ค๋ฅธ ๋ณ๊ฒฝ์ ํ์ํ์ง ์์์ต๋๋ค(์๋ฅผ ๋ค์ด, ์์ญ์ด ํจ์น threads
๋ฅผ ๋นํ์ฑํํ ํ์๊ฐ ์์ต๋๋ค).
๋๋ ์ฌ์ ํ ๋ง์คํฐ ํ๋ก์ธ์ค๋ฅผ ์์ญ์ด ํจ์นํ๋ ๊ฒ์ ๋งค์ฐ ์ฃผ์ ํ๊ฒ ์ง๋ง, ๋ง์ฝ ๋น์ ์ด ๊ทธ๊ฒ์ ํ๊ณ ์ถ๋ค๋ฉด gunicorn ์์ฒด๋ฅผ ํจ์นํ๋ ๋์ ์๋ฒ ํํฌ๋ฅผ ์ฌ์ฉํ ์ ์์ ๊ฒ์ ๋๋ค. ๊ทธ๋ ๊ฒ ํ ์ ์๋ค๋ฉด ์ฌ๊ธฐ๋ณด๋ค ๋ ๋์ ๊ณณ์์ ๋ฌธ์ํํ ์ ์๋ ๋ฐฉ๋ฒ์ ํ์ํฉ๋๋ค.
๋จ๊ธฐ ๊ณํ์ ์ด ํจ์น๊ฐ ์ ์ฉ๋ (๋ด๋ถ) gunicorn ํฌํฌ๋ฅผ ๋ง๋๋ ๊ฒ์ ๋๋ค.
๋๋ ์์ญ์ด ํจ์น ๋จ๊ณ๋ฅผ wsgi ์ฝ๋๋ก ์ฎ๊ธฐ๋ ๊ฒ์ ์กฐ์ฌํ ๊ฒ์ด์ง๋ง, ์ด๊ฒ์ ์ฌ์ ํ โโgunicorn ์ฝ๋ ๋ด์์ ์ํ๋์ด์ผ ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ ๋๋ค(๊ตฌ์ฑ ๊ฐ๋ฅํ ์ต์ ์ผ๋ก๋). ์ฌ์ ๋ก๋๋ ์ฑ ์ฝ๋์ ํจ์น๋ ๋ง์คํฐ์ ์ด๋ค ์์ผ๋ก๋ ์ํฅ์ ๋ฏธ์น๋ฉฐ ์์ ์ ์์ ์์ ์์ญ์ด ํจ์น ์ํ๋ฅผ ๋ง์คํฐ์ ๋๊ธฐํํ๋๋ก ์ ์งํ๋ ๊ฒ์ ๋๋ค. ์ฑ ์ฝ๋์ ๋ง์คํฐ ์์ญ์ด ํจ์น๋ฅผ ์ ์ฉํ๋ค๋ ๊ฒ์ ์ด ๋์์ ์์กดํ๋ ์ฑ ๊ฐ๋ฐ์๊ฐ ์์ญ์ด๊ฐ ์์ ์๋ฅผ ํจ์นํ๋ ์๊ธฐ/๋ฐฉ๋ฒ์ ๋ํด gunicorn์ด ๋ง๋๋ ๋ชจ๋ ๋ณ๊ฒฝ ์ฌํญ์ ํญ์ ๋ฐ๋ผ์ก๋๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
ํจ์น๊ฐ ์ผ์ฐ ์๋ํ๋ค๋ฉด ์๋ง๋ ๋ณ๊ฒฝ ์ฌํญ์ผ๋ก ๊ฐ์ฃผ๋์ด์ผ ํ ๊ฒ์ ๋๋ค.
๊ทธ๊ฒ์ด ์ฌ์ค์ธ์ง, ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ด ๋ฌธ์ ๋ฅผ ์ผ์ผ์ผฐ๋์ง ๊ธฐ์ต๋์ง ์์ง๋ง, ์์ญ์ด ํจ์น์์๋ ํญ์ ๊น๋ค๋กญ์ต๋๋ค.
์ฐ๋ฆฌ ์์ ์ ํ๋ก๋์
gunicorn ์์ฉ ํ๋ก๊ทธ๋จ ์ ์ฌ์ ๋ก๋ ํ์ํฉ๋๋ค (์ฐ๋ฆฌ๋ Pyramid/paste์ pserve
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ์ ์์ํ๊ณ ini ํ์ผ์์ gunicorn์ ๋ก๋ํฉ๋๋ค. ์์ญ์ด ํจ์น๊ฐ ์ต๋ํ ๋นจ๋ฆฌ ๋ฐ์ํ๋์ง ํ์ธ).
์ ๋ ํ์ฌ gevent ๊ด๋ฆฌ์์ ๋๋ค. ์ด ์๋๋ฆฌ์ค๊ฐ ๋ ์ ์๋ํ๋๋ก ํ๋ ๋ฐ ํ์ํ gevent์ ๋ชจ๋ ๋ฌธ์ ๋ฅผ ์ดํด๋ณด๊ฒ ๋์ด ๊ธฐ์ฉ๋๋ค. ์ฐ๋ฆฌ๋ ๊ณง 1.1์ ๋๋กญํ ๊ฒ์ผ๋ก ์์ํ๋ฉฐ ์ด๊ฒ์ด ์ง์๋๋ ์ต์ ์ด๋ผ๋ฉด ์ข์ ๊ฒ์ ๋๋ค.
๋๋ wsgi ๊ตฌํ์ ๋งจ ์์ ๋์ผํ ์์ญ์ด ํจ์น ๋
ผ๋ฆฌ๋ฅผ ์ ์ฉํ๋ ค๊ณ ์๋ํ๊ณ DatabaseWrapper
๋ฌธ์ ๋ฅผ ๋ณด๊ธฐ ์์ํ์ต๋๋ค. ์ด์ ๋ ํ์คํ์ง ์์ง๋ง - arbiter.py ์์ wsgi ํธ์ถ์ ๋ณด๋ฉด GeventWorker.setup
๊ฐ ํธ์ถ๋๋ ์ง์ ๋ช ์ค ๋ค์ ๋ฐ์ํฉ๋๋ค( worker_class ํ ๋น ์
์ ๋ ์นด๊ฐ ์๊ฑฐ๋ ์ฑ ์ฝ๋์ ๊ด๋ จํ์ฌ ์ด์ด ์ข์ผ๋ฉด ํ์ ์กฐ์น๋ฅผ ์ทจํ๊ฒ ์ต๋๋ค. ๊ทธ๋ฌ๋ ์ง๊ธ๊น์ง๋ gunicorn ๋ง์คํฐ ์ฝ๋์์ ๋ ์ผ์ฐ ํจ์นํ๋ ๊ฒ์ด ๋ชจ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์ ์ผํ ์์ ์ฌํญ์ ๋๋ค.
@jamadden ์ ๋ง ๊ฐ์ฌํฉ๋๋ค.
@jzupko ์ผ๋ฐ์ ์ผ๋ก ์ํฅ์ ๋ฐ์ ์ ์๋ ๊ฐ์ ธ์ค๊ธฐ ์ ์ ํจ์น๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๊ทธ๋์ gunicorn.conf.py
ํ์ผ์ post_fork
ํจ์๋ฅผ ๋ฃ๋ ๊ฒ๊ณผ ๊ฐ์ด ์๋ฒ ํํฌ์์ ์ํํ ๊ฒ์ ์ ์ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ์์ฉ ํ๋ก๊ทธ๋จ ์ฌ์ ๋ก๋๊ฐ ์๋ฒ ํํฌ๋ณด๋ค ๋จผ์ ๋ฐ์ํ๊ณ ์ด๋์์ ์ํํ ์ง ๋ช
ํํ์ง ์๋ค๋ ๊ฒ์ ๋ฐฉ๊ธ ๊นจ๋ฌ์์ต๋๋ค.
@benoitc ์์ ์๋ณ ๊ตฌ์ฑ์ ์ํ ๋ฉ์ปค๋์ฆ์ด ์์ต๋๊น?
๋๋ ์ด๊ฒ์ ๋ฆด๋ฆฌ์ค 20 ์ด์ ํ์ ์ถ๊ฐํ๊ณ #478๊ณผ ๊ฐ์ ๋ฌธ์ ๋ฅผ ๋ค์ ๋์ ํ์ง ์๊ณ ์ํํ ์ ์๋ค๋ฉด ์์ญ์ด๊ฐ ๋ง์คํฐ๋ฅผ ํจ์นํ๋ ๊ฒ์ ๊ณ ๋ คํ ๊ฒ์ ์ ์ํฉ๋๋ค.
@tilgovi ๋ ์์
์์ setup
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
20.0์ด ๋์ฌ ๋๊น์ง ์์๋ก ์์ ํ ์ฌ๋์ด ์์ต๋๊น? ์๋๋ฉด preload_app
๋นํ์ฑํ ๋๋ 18.0์ผ๋ก ๋ค์ด๊ทธ๋ ์ด๋ ์ค์์ ์ ํํ ์ ์๋ ์ ์ผํ ์๋ฃจ์
์
๋๊น?
@fletom ์์ ์์ ์ฌํญ? ์ต์ ๋ง์คํฐ๋ฅผ ์ฌ์ฉํด ๋ณด์ จ์ต๋๊น? ๊ทธ๊ฒ์ ๋น์ ์ ์ํด ์๋ ํ์ต๋๊น?
@benoitc "DatabaseWrapper objects" ์ค๋ฅ์ ๋ํ ์์ ์ ์๋ฏธํฉ๋๋ค.
ํ์ฌ gunicorn ๋ง์คํฐ๋ ์ค์ ๋ก ์ ํ ์๋ํ์ง ์์ต๋๋ค. ์ผ๋ฐ 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 gunicorn์ ์ด๋ป๊ฒ ์์ํฉ๋๊น?
@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๊ฐ ์ ์ํ ์์ ์ฌํญ์ด ํจ๊ณผ๊ฐ ์์์ง๋ง ์ฌ์ ํ ๋์ผํ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค ๐
๋๋ ์ด๊ฒ์ ๋ซ๊ณ gevent์์ ์ค์ฌ์๋ฅผ ์์ญ์ด ํจ์นํ๊ธฐ ์ํ R20 ์ด์ ํ์ ๋ฉ์ผ๋ง ๋ฆฌ์คํธ ๋ ์ด๋ธ๊ณผ ํ ๋ก ์ ์ด ๊ฒ์ ๋๋ค. ์ด ํ ๋ก ๊ณผ ๊ด๋ จ๋ ๋ฌธ์ ๊ฐ ์๊ณ Gunicorn์ ๋ฒ๊ทธ์ผ ์ ์๋ค๊ณ ์๊ฐ๋๋ฉด ๋ฌธ์ ๋ฅผ ์ฌ์ญ์์ค. ๋งฅ๋ฝ์ ์ถ๊ฐํ๋ ๋ฐ ๋์์ด ๋๋ค๋ฉด ์ด๊ฒ์ ๋ฒํธ๋ก ์ธ๊ธํ๊ฑฐ๋ ๋๊ธ์ ๋งํฌํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ์์ ์์ ์ด๊ฒ์ ์ฌ๋ฌ ๋ฒ์ ์ Gunicorn ๋ฐ ๋ค์ํ ์ฆ์์ ๋ํ ๋ํ๊ฐ ์๋ ์ค๋๋ ํฐ์ผ์ ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
@gukoff๊ฐ ์ ์ํ ์์ ์ฌํญ์ด ํจ๊ณผ๊ฐ ์์์ง๋ง ์ฌ์ ํ ๋์ผํ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค ๐