root / snf-pithos-backend / pithos / workers / gevent_archipelago.py @ 06a33909
History | View | Annotate | Download (5.3 kB)
1 |
# -*- coding: utf-8 -
|
---|---|
2 |
#
|
3 |
# Copyright 2013 GRNET S.A. All rights reserved.
|
4 |
#
|
5 |
# Redistribution and use in source and binary forms, with or
|
6 |
# without modification, are permitted provided that the following
|
7 |
# conditions are met:
|
8 |
#
|
9 |
# 1. Redistributions of source code must retain the above
|
10 |
# copyright notice, this list of conditions and the following
|
11 |
# disclaimer.
|
12 |
#
|
13 |
# 2. Redistributions in binary form must reproduce the above
|
14 |
# copyright notice, this list of conditions and the following
|
15 |
# disclaimer in the documentation and/or other materials
|
16 |
# provided with the distribution.
|
17 |
#
|
18 |
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
19 |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20 |
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
21 |
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
22 |
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23 |
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24 |
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
25 |
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
26 |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
27 |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
28 |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29 |
# POSSIBILITY OF SUCH DAMAGE.
|
30 |
#
|
31 |
# The views and conclusions contained in the software and
|
32 |
# documentation are those of the authors and should not be
|
33 |
# interpreted as representing official policies, either expressed
|
34 |
# or implied, of GRNET S.A.
|
35 |
|
36 |
|
37 |
from __future__ import with_statement |
38 |
|
39 |
import os |
40 |
import sys |
41 |
from datetime import datetime |
42 |
|
43 |
# workaround on osx, disable kqueue
|
44 |
if sys.platform == "darwin": |
45 |
os.environ['EVENT_NOKQUEUE'] = "1" |
46 |
|
47 |
try:
|
48 |
import gevent |
49 |
except ImportError: |
50 |
raise RuntimeError("You need gevent installed to use this worker.") |
51 |
from gevent.pool import Pool |
52 |
from gevent.server import StreamServer |
53 |
from gevent import pywsgi |
54 |
from gevent import select |
55 |
|
56 |
import gunicorn |
57 |
from gunicorn.workers.async import AsyncWorker |
58 |
|
59 |
VERSION = "gevent/%s gunicorn/%s" % (gevent.__version__, gunicorn.__version__)
|
60 |
|
61 |
BASE_WSGI_ENV = { |
62 |
'GATEWAY_INTERFACE': 'CGI/1.1', |
63 |
'SERVER_SOFTWARE': VERSION,
|
64 |
'SCRIPT_NAME': '', |
65 |
'wsgi.version': (1, 0), |
66 |
'wsgi.multithread': False, |
67 |
'wsgi.multiprocess': False, |
68 |
'wsgi.run_once': False |
69 |
} |
70 |
|
71 |
|
72 |
class GeventArchipelagoWorker(AsyncWorker): |
73 |
|
74 |
server_class = None
|
75 |
wsgi_handler = None
|
76 |
|
77 |
def __init__(self, *args, **kwargs): |
78 |
super(GeventArchipelagoWorker, self).__init__(*args, **kwargs) |
79 |
self.worker_id = args[0] |
80 |
|
81 |
@classmethod
|
82 |
def setup(cls): |
83 |
from gevent import monkey |
84 |
monkey.noisy = False
|
85 |
monkey.patch_all() |
86 |
|
87 |
def timeout_ctx(self): |
88 |
return gevent.Timeout(self.cfg.keepalive, False) |
89 |
|
90 |
def run(self): |
91 |
self.socket.setblocking(1) |
92 |
|
93 |
pool = Pool(self.worker_connections)
|
94 |
if self.server_class is not None: |
95 |
server = self.server_class(
|
96 |
self.socket, application=self.wsgi, spawn=pool, log=self.log, |
97 |
handler_class=self.wsgi_handler)
|
98 |
else:
|
99 |
server = StreamServer(self.socket, handle=self.handle, spawn=pool) |
100 |
|
101 |
server.start() |
102 |
try:
|
103 |
while self.alive: |
104 |
self.notify()
|
105 |
if self.ppid != os.getppid(): |
106 |
self.log.info("Parent changed, shutting down: %s", self) |
107 |
break
|
108 |
|
109 |
gevent.sleep(1.0)
|
110 |
|
111 |
except KeyboardInterrupt: |
112 |
pass
|
113 |
|
114 |
try:
|
115 |
# Try to stop connections until timeout
|
116 |
self.notify()
|
117 |
server.stop(timeout=self.cfg.graceful_timeout)
|
118 |
except:
|
119 |
pass
|
120 |
|
121 |
def handle_request(self, *args): |
122 |
try:
|
123 |
super(GeventArchipelagoWorker, self).handle_request(*args) |
124 |
except gevent.GreenletExit:
|
125 |
pass
|
126 |
|
127 |
if gevent.version_info[0] == 0: |
128 |
|
129 |
def init_process(self): |
130 |
#gevent 0.13 and older doesn't reinitialize dns for us after
|
131 |
#forking here's the workaround
|
132 |
import gevent.core |
133 |
gevent.core.dns_shutdown(fail_requests=1)
|
134 |
gevent.core.dns_init() |
135 |
super(GeventArchipelagoWorker, self).init_process() |
136 |
|
137 |
|
138 |
class GeventResponse(object): |
139 |
|
140 |
status = None
|
141 |
headers = None
|
142 |
response_length = None
|
143 |
|
144 |
def __init__(self, status, headers, clength): |
145 |
self.status = status
|
146 |
self.headers = headers
|
147 |
self.response_length = clength
|
148 |
|
149 |
|
150 |
class PyWSGIHandler(pywsgi.WSGIHandler): |
151 |
|
152 |
def log_request(self): |
153 |
start = datetime.fromtimestamp(self.time_start)
|
154 |
finish = datetime.fromtimestamp(self.time_finish)
|
155 |
response_time = finish - start |
156 |
resp = GeventResponse(self.status, self.response_headers, |
157 |
self.response_length)
|
158 |
req_headers = [h.split(":", 1) for h in self.headers.headers] |
159 |
self.server.log.access(resp, req_headers, self.environ, response_time) |
160 |
|
161 |
def get_environ(self): |
162 |
env = super(PyWSGIHandler, self).get_environ() |
163 |
env['gunicorn.sock'] = self.socket |
164 |
env['RAW_URI'] = self.path |
165 |
return env
|
166 |
|
167 |
|
168 |
class PyWSGIServer(pywsgi.WSGIServer): |
169 |
base_env = BASE_WSGI_ENV |
170 |
|
171 |
|
172 |
class GeventPyWSGIWorker(GeventArchipelagoWorker): |
173 |
"The Gevent StreamServer based workers."
|
174 |
server_class = PyWSGIServer |
175 |
wsgi_handler = PyWSGIHandler |