mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Merge pull request #257 from tilgovi/master
some improvements to gevent code
This commit is contained in:
commit
f7dd412bc3
@ -93,7 +93,7 @@ class Logger(object):
|
|||||||
'r': "%s %s %s" % (environ['REQUEST_METHOD'],
|
'r': "%s %s %s" % (environ['REQUEST_METHOD'],
|
||||||
environ['RAW_URI'], environ["SERVER_PROTOCOL"]),
|
environ['RAW_URI'], environ["SERVER_PROTOCOL"]),
|
||||||
's': status,
|
's': status,
|
||||||
'b': str(resp.clength) or '-',
|
'b': str(resp.response_length) or '-',
|
||||||
'f': environ.get('HTTP_REFERER', '-'),
|
'f': environ.get('HTTP_REFERER', '-'),
|
||||||
'a': environ.get('HTTP_USER_AGENT', '-'),
|
'a': environ.get('HTTP_USER_AGENT', '-'),
|
||||||
'T': str(request_time.seconds),
|
'T': str(request_time.seconds),
|
||||||
|
|||||||
@ -61,25 +61,25 @@ class Message(object):
|
|||||||
|
|
||||||
def set_body_reader(self):
|
def set_body_reader(self):
|
||||||
chunked = False
|
chunked = False
|
||||||
clength = None
|
response_length = None
|
||||||
for (name, value) in self.headers:
|
for (name, value) in self.headers:
|
||||||
if name == "CONTENT-LENGTH":
|
if name == "CONTENT-LENGTH":
|
||||||
try:
|
try:
|
||||||
clength = int(value)
|
response_length = int(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
clength = None
|
response_length = None
|
||||||
elif name == "TRANSFER-ENCODING":
|
elif name == "TRANSFER-ENCODING":
|
||||||
chunked = value.lower() == "chunked"
|
chunked = value.lower() == "chunked"
|
||||||
elif name == "SEC-WEBSOCKET-KEY1":
|
elif name == "SEC-WEBSOCKET-KEY1":
|
||||||
clength = 8
|
response_length = 8
|
||||||
|
|
||||||
if clength is not None or chunked:
|
if response_length is not None or chunked:
|
||||||
break
|
break
|
||||||
|
|
||||||
if chunked:
|
if chunked:
|
||||||
self.body = Body(ChunkedReader(self, self.unreader))
|
self.body = Body(ChunkedReader(self, self.unreader))
|
||||||
elif clength is not None:
|
elif response_length is not None:
|
||||||
self.body = Body(LengthReader(self.unreader, clength))
|
self.body = Body(LengthReader(self.unreader, response_length))
|
||||||
else:
|
else:
|
||||||
self.body = Body(EOFReader(self.unreader))
|
self.body = Body(EOFReader(self.unreader))
|
||||||
|
|
||||||
|
|||||||
@ -154,7 +154,7 @@ class Response(object):
|
|||||||
self.must_close = False
|
self.must_close = False
|
||||||
self.headers = []
|
self.headers = []
|
||||||
self.headers_sent = False
|
self.headers_sent = False
|
||||||
self.clength = None
|
self.response_length = None
|
||||||
self.sent = 0
|
self.sent = 0
|
||||||
|
|
||||||
def force_close(self):
|
def force_close(self):
|
||||||
@ -163,7 +163,7 @@ class Response(object):
|
|||||||
def should_close(self):
|
def should_close(self):
|
||||||
if self.must_close or self.req.should_close():
|
if self.must_close or self.req.should_close():
|
||||||
return True
|
return True
|
||||||
if self.clength is not None or self.chunked:
|
if self.response_length is not None or self.chunked:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ class Response(object):
|
|||||||
assert isinstance(name, basestring), "%r is not a string" % name
|
assert isinstance(name, basestring), "%r is not a string" % name
|
||||||
lname = name.lower().strip()
|
lname = name.lower().strip()
|
||||||
if lname == "content-length":
|
if lname == "content-length":
|
||||||
self.clength = int(value)
|
self.response_length = int(value)
|
||||||
elif util.is_hoppish(name):
|
elif util.is_hoppish(name):
|
||||||
if lname == "connection":
|
if lname == "connection":
|
||||||
# handle websocket
|
# handle websocket
|
||||||
@ -203,7 +203,7 @@ class Response(object):
|
|||||||
# Only use chunked responses when the client is
|
# Only use chunked responses when the client is
|
||||||
# speaking HTTP/1.1 or newer and there was
|
# speaking HTTP/1.1 or newer and there was
|
||||||
# no Content-Length header set.
|
# no Content-Length header set.
|
||||||
if self.clength is not None:
|
if self.response_length is not None:
|
||||||
return False
|
return False
|
||||||
elif self.req.version <= (1,0):
|
elif self.req.version <= (1,0):
|
||||||
return False
|
return False
|
||||||
@ -242,12 +242,12 @@ class Response(object):
|
|||||||
|
|
||||||
arglen = len(arg)
|
arglen = len(arg)
|
||||||
tosend = arglen
|
tosend = arglen
|
||||||
if self.clength is not None:
|
if self.response_length is not None:
|
||||||
if self.sent >= self.clength:
|
if self.sent >= self.response_length:
|
||||||
# Never write more than self.clength bytes
|
# Never write more than self.response_length bytes
|
||||||
return
|
return
|
||||||
|
|
||||||
tosend = min(self.clength - self.sent, tosend)
|
tosend = min(self.response_length - self.sent, tosend)
|
||||||
if tosend < arglen:
|
if tosend < arglen:
|
||||||
arg = arg[:tosend]
|
arg = arg[:tosend]
|
||||||
|
|
||||||
@ -287,8 +287,8 @@ class Response(object):
|
|||||||
fo_offset = respiter.filelike.tell()
|
fo_offset = respiter.filelike.tell()
|
||||||
nbytes = max(os.fstat(fileno).st_size - fo_offset, 0)
|
nbytes = max(os.fstat(fileno).st_size - fo_offset, 0)
|
||||||
|
|
||||||
if self.clength:
|
if self.response_length:
|
||||||
nbytes = min(nbytes, self.clength)
|
nbytes = min(nbytes, self.response_length)
|
||||||
|
|
||||||
if nbytes == 0:
|
if nbytes == 0:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -7,6 +7,7 @@ from __future__ import with_statement
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# workaround on osx, disable kqueue
|
# workaround on osx, disable kqueue
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
@ -36,19 +37,6 @@ BASE_WSGI_ENV = {
|
|||||||
'wsgi.run_once': False
|
'wsgi.run_once': False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GGeventServer(StreamServer):
|
|
||||||
def __init__(self, listener, handle, spawn='default', worker=None):
|
|
||||||
StreamServer.__init__(self, listener, spawn=spawn)
|
|
||||||
self.handle_func = handle
|
|
||||||
self.worker = worker
|
|
||||||
|
|
||||||
def stop(self, timeout=None):
|
|
||||||
super(GGeventServer, self).stop(timeout=timeout)
|
|
||||||
|
|
||||||
def handle(self, sock, addr):
|
|
||||||
self.handle_func(sock, addr)
|
|
||||||
|
|
||||||
class GeventWorker(AsyncWorker):
|
class GeventWorker(AsyncWorker):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -65,8 +53,12 @@ class GeventWorker(AsyncWorker):
|
|||||||
self.socket.setblocking(1)
|
self.socket.setblocking(1)
|
||||||
|
|
||||||
pool = Pool(self.worker_connections)
|
pool = Pool(self.worker_connections)
|
||||||
server = GGeventServer(self.socket, self.handle, spawn=pool,
|
if hasattr(self, 'server_class'):
|
||||||
worker=self)
|
server = self.server_class(
|
||||||
|
self.socket, application=self.wsgi, spawn=pool, log=self.log,
|
||||||
|
handler_class=self.wsgi_handler)
|
||||||
|
else:
|
||||||
|
server = StreamServer(self.socket, handle=self.handle, spawn=pool)
|
||||||
|
|
||||||
server.start()
|
server.start()
|
||||||
try:
|
try:
|
||||||
@ -103,70 +95,13 @@ class GeventWorker(AsyncWorker):
|
|||||||
gevent.core.dns_init()
|
gevent.core.dns_init()
|
||||||
super(GeventWorker, self).init_process()
|
super(GeventWorker, self).init_process()
|
||||||
|
|
||||||
|
|
||||||
class GeventBaseWorker(Worker):
|
|
||||||
"""\
|
|
||||||
This base class is used for the two variants of workers that use
|
|
||||||
Gevent's two different WSGI workers. ``gevent_wsgi`` worker uses
|
|
||||||
the libevent HTTP parser but does not support streaming response
|
|
||||||
bodies or Keep-Alive. The ``gevent_pywsgi`` worker uses an
|
|
||||||
alternative Gevent WSGI server that supports streaming and Keep-
|
|
||||||
Alive but does not use the libevent HTTP parser.
|
|
||||||
"""
|
|
||||||
server_class = None
|
|
||||||
wsgi_handler = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(GeventBaseWorker, self).__init__(*args, **kwargs)
|
|
||||||
self.worker_connections = self.cfg.worker_connections
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setup(cls):
|
|
||||||
from gevent import monkey
|
|
||||||
monkey.noisy = False
|
|
||||||
monkey.patch_all()
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.socket.setblocking(1)
|
|
||||||
pool = Pool(self.worker_connections)
|
|
||||||
self.server_class.base_env['wsgi.multiprocess'] = (self.cfg.workers > 1)
|
|
||||||
server = self.server_class(self.socket, application=self.wsgi,
|
|
||||||
spawn=pool, handler_class=self.wsgi_handler)
|
|
||||||
server.start()
|
|
||||||
try:
|
|
||||||
while self.alive:
|
|
||||||
self.notify()
|
|
||||||
|
|
||||||
if self.ppid != os.getppid():
|
|
||||||
self.log.info("Parent changed, shutting down: %s", self)
|
|
||||||
break
|
|
||||||
|
|
||||||
gevent.sleep(1.0)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# try to stop the connections
|
|
||||||
try:
|
|
||||||
self.notify()
|
|
||||||
server.stop(timeout=self.timeout)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if hasattr(gevent.core, 'dns_shutdown'):
|
|
||||||
|
|
||||||
def init_process(self):
|
|
||||||
#gevent 0.13 and older doesn't reinitialize dns for us after forking
|
|
||||||
#here's the workaround
|
|
||||||
gevent.core.dns_shutdown(fail_requests=1)
|
|
||||||
gevent.core.dns_init()
|
|
||||||
super(GeventBaseWorker, self).init_process()
|
|
||||||
|
|
||||||
|
|
||||||
class PyWSGIHandler(pywsgi.WSGIHandler):
|
class PyWSGIHandler(pywsgi.WSGIHandler):
|
||||||
def log_request(self, *args):
|
|
||||||
pass
|
def log_request(self):
|
||||||
|
start = datetime.fromtimestamp(self.time_start)
|
||||||
|
finish = datetime.fromtimestamp(self.time_finish)
|
||||||
|
response_time = finish - start
|
||||||
|
self.server.log.access(self, self.environ, response_time)
|
||||||
|
|
||||||
def get_environ(self):
|
def get_environ(self):
|
||||||
env = super(PyWSGIHandler, self).get_environ()
|
env = super(PyWSGIHandler, self).get_environ()
|
||||||
@ -177,7 +112,7 @@ class PyWSGIHandler(pywsgi.WSGIHandler):
|
|||||||
class PyWSGIServer(pywsgi.WSGIServer):
|
class PyWSGIServer(pywsgi.WSGIServer):
|
||||||
base_env = BASE_WSGI_ENV
|
base_env = BASE_WSGI_ENV
|
||||||
|
|
||||||
class GeventPyWSGIWorker(GeventBaseWorker):
|
class GeventPyWSGIWorker(GeventWorker):
|
||||||
"The Gevent StreamServer based workers."
|
"The Gevent StreamServer based workers."
|
||||||
server_class = PyWSGIServer
|
server_class = PyWSGIServer
|
||||||
wsgi_handler = PyWSGIHandler
|
wsgi_handler = PyWSGIHandler
|
||||||
|
|||||||
@ -3,24 +3,38 @@
|
|||||||
# This file is part of gunicorn released under the MIT license.
|
# This file is part of gunicorn released under the MIT license.
|
||||||
# See the NOTICE for more information.
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
from gunicorn.workers.ggevent import BASE_WSGI_ENV, GeventBaseWorker
|
from datetime import datetime
|
||||||
|
|
||||||
|
from gunicorn.workers.ggevent import BASE_WSGI_ENV, GeventWorker
|
||||||
from gevent import wsgi
|
from gevent import wsgi
|
||||||
|
|
||||||
|
|
||||||
class WSGIHandler(wsgi.WSGIHandler):
|
class WSGIHandler(wsgi.WSGIHandler):
|
||||||
def log_request(self, *args):
|
|
||||||
pass
|
@property
|
||||||
|
def status(self):
|
||||||
|
return ' '.join([str(self.code), self.reason])
|
||||||
|
|
||||||
|
def log_request(self, length):
|
||||||
|
self.response_length = length
|
||||||
|
response_time = datetime.now() - self.time_start
|
||||||
|
self.server.log.access(self, self.environ, response_time)
|
||||||
|
|
||||||
def prepare_env(self):
|
def prepare_env(self):
|
||||||
env = super(WSGIHandler, self).prepare_env()
|
env = super(WSGIHandler, self).prepare_env()
|
||||||
env['RAW_URI'] = self.request.uri
|
env['RAW_URI'] = self.request.uri
|
||||||
|
self.environ = env
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
self.time_start = datetime.now()
|
||||||
|
super(WSGIHandler, self).handle()
|
||||||
|
|
||||||
|
|
||||||
class WSGIServer(wsgi.WSGIServer):
|
class WSGIServer(wsgi.WSGIServer):
|
||||||
base_env = BASE_WSGI_ENV
|
base_env = BASE_WSGI_ENV
|
||||||
|
|
||||||
class GeventWSGIWorker(GeventBaseWorker):
|
class GeventWSGIWorker(GeventWorker):
|
||||||
"The Gevent StreamServer based workers."
|
"The Gevent StreamServer based workers."
|
||||||
server_class = WSGIServer
|
server_class = WSGIServer
|
||||||
wsgi_handler = WSGIHandler
|
wsgi_handler = WSGIHandler
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user