From 0577135a76dfec09a05eb92d59d6a09ccaa43913 Mon Sep 17 00:00:00 2001 From: benoitc Date: Mon, 4 Jun 2012 23:41:53 +0200 Subject: [PATCH 1/5] apparently gevent doesn4t import gevent.core now --- gunicorn/workers/ggevent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gunicorn/workers/ggevent.py b/gunicorn/workers/ggevent.py index 53629da2..c92546c5 100644 --- a/gunicorn/workers/ggevent.py +++ b/gunicorn/workers/ggevent.py @@ -92,6 +92,7 @@ class GeventWorker(AsyncWorker): def init_process(self): #gevent 0.13 and older doesn't reinitialize dns for us after forking #here's the workaround + import gevent.core gevent.core.dns_shutdown(fail_requests=1) gevent.core.dns_init() super(GeventWorker, self).init_process() From d7caa526e5cd7feb4349fa19b3b6c27dc7e0d351 Mon Sep 17 00:00:00 2001 From: Caleb Brown Date: Tue, 29 May 2012 17:43:59 +1000 Subject: [PATCH 2/5] If keepalive is 0 use None so the timeout is ignored. --- gunicorn/workers/geventlet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gunicorn/workers/geventlet.py b/gunicorn/workers/geventlet.py index 948257b1..cc4df058 100644 --- a/gunicorn/workers/geventlet.py +++ b/gunicorn/workers/geventlet.py @@ -30,7 +30,7 @@ class EventletWorker(AsyncWorker): super(EventletWorker, self).init_process() def timeout_ctx(self): - return eventlet.Timeout(self.cfg.keepalive, False) + return eventlet.Timeout(self.cfg.keepalive or None, False) def run(self): self.socket = GreenSocket(family_or_realsock=self.socket.sock) From c1b073a7accc68755a1eabbf77687f2b09e37498 Mon Sep 17 00:00:00 2001 From: benoitc Date: Wed, 6 Jun 2012 09:56:40 +0200 Subject: [PATCH 3/5] add Caleb Brown to THANKS --- THANKS | 1 + 1 file changed, 1 insertion(+) diff --git a/THANKS b/THANKS index e7a6236c..57a51dd9 100644 --- a/THANKS +++ b/THANKS @@ -41,3 +41,4 @@ Maxim Kamenkov Konstantin Kapustin Djoume Salvetti ZheFu Peng (CMGS) +Caleb Brown From da637dfd13b520fc190b86967dfecc06bf97a2b4 Mon Sep 17 00:00:00 2001 From: benoitc Date: Mon, 4 Jun 2012 21:17:11 +0200 Subject: [PATCH 4/5] fix issue #348 . Rather than testing the parent pd, test if the parent pid is still alive. Only use it in gevent for now. --- .../testing/apps/someapp/middleware.py | 22 +++++++++++++++++++ .../django/testing/testing/settings.py | 1 + gunicorn/util.py | 7 ++++++ gunicorn/workers/ggevent.py | 7 ++++-- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 examples/frameworks/django/testing/testing/apps/someapp/middleware.py diff --git a/examples/frameworks/django/testing/testing/apps/someapp/middleware.py b/examples/frameworks/django/testing/testing/apps/someapp/middleware.py new file mode 100644 index 00000000..a38ec5ac --- /dev/null +++ b/examples/frameworks/django/testing/testing/apps/someapp/middleware.py @@ -0,0 +1,22 @@ +from multiprocessing import Process, Queue +import requests + +def child_process(queue): + while True: + print queue.get() + r = requests.get('http://friendpaste.com') + print r.headers + +class GunicornSubProcessTestMiddleware(object): + def __init__(self): + super(GunicornSubProcessTestMiddleware, self).__init__() + self.queue = Queue() + self.process = Process(target=child_process, args=(self.queue,)) + self.process.start() + + def process_request(self, request): + self.queue.put(('REQUEST',)) + + def process_response(self, request, response): + self.queue.put(('RESPONSE',response.status_code)) + return response diff --git a/examples/frameworks/django/testing/testing/settings.py b/examples/frameworks/django/testing/testing/settings.py index cb8eca68..3a752c71 100644 --- a/examples/frameworks/django/testing/testing/settings.py +++ b/examples/frameworks/django/testing/testing/settings.py @@ -98,6 +98,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', # Uncomment the next line for simple clickjacking protection: # 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'testing.apps.someapp.middleware.GunicornSubProcessTestMiddleware' ) ROOT_URLCONF = 'testing.urls' diff --git a/gunicorn/util.py b/gunicorn/util.py index e919d53c..748ce2d6 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -353,3 +353,10 @@ def check_is_writeable(path): except IOError, e: raise RuntimeError("Error: '%s' isn't writable [%r]" % (path, e)) f.close() + +def is_process_running(process_id): + try: + os.kill(process_id, 0) + return True + except OSError: + return False diff --git a/gunicorn/workers/ggevent.py b/gunicorn/workers/ggevent.py index c92546c5..5b35bdc3 100644 --- a/gunicorn/workers/ggevent.py +++ b/gunicorn/workers/ggevent.py @@ -23,6 +23,7 @@ from gevent import pywsgi import gunicorn from gunicorn.workers.async import AsyncWorker +from gunicorn.util import is_process_running VERSION = "gevent/%s gunicorn/%s" % (gevent.__version__, gunicorn.__version__) @@ -36,6 +37,7 @@ BASE_WSGI_ENV = { 'wsgi.run_once': False } + class GeventWorker(AsyncWorker): server_class = None @@ -61,11 +63,12 @@ class GeventWorker(AsyncWorker): else: server = StreamServer(self.socket, handle=self.handle, spawn=pool) - server.start() + gevent.spawn(server.start) try: while self.alive: self.notify() - if self.ppid != os.getppid(): + + if not is_process_running(self.ppid): self.log.info("Parent changed, shutting down: %s", self) break From a68618c824216018a6560af8db89493c574328a0 Mon Sep 17 00:00:00 2001 From: benoitc Date: Mon, 18 Jun 2012 11:02:30 +0200 Subject: [PATCH 5/5] breaking change: take the control on graceful reload back We really shouldn't allow the people to override the way we spawn the new workers on reload. Graceful is about launching new worker and kill olders after the graceful time. --- gunicorn/arbiter.py | 6 +++++- gunicorn/config.py | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index 47f9888e..802abda8 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -359,7 +359,7 @@ class Arbiter(object): self.LISTENER = create_socket(self.cfg, self.log) self.log.info("Listening at: %s", self.LISTENER) - # spawn new workers with new app & conf + # do some actions on reload self.cfg.on_reload(self) # unlink pidfile @@ -374,6 +374,10 @@ class Arbiter(object): # set new proc_name util._setproctitle("master [%s]" % self.proc_name) + # spawn new workers + for i in range(self.cfg.workers): + self.spawn_worker() + # manage workers self.manage_workers() diff --git a/gunicorn/config.py b/gunicorn/config.py index 58848215..df4d8076 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -867,8 +867,7 @@ class OnReload(Setting): validator = validate_callable(1) type = "callable" def on_reload(server): - for i in range(server.app.cfg.workers): - server.spawn_worker() + pass default = staticmethod(on_reload) desc = """\ Called to recycle workers during a reload via SIGHUP.