mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
allows gunicorn to bind to multiple address
Allows gunicorn to listen on different interface. It can be either ipv6,
unix or ipv4 sockets.
Ex:
gunicorn -w3 -b 127.0.0.1:8001 -b 127.0.0.1:8000 -b [::1]:8000 test:app
fix #444
This commit is contained in:
parent
41a6999fa3
commit
b7b51adf13
@ -12,8 +12,7 @@ error_email_from = paste@localhost
|
|||||||
|
|
||||||
[server:main]
|
[server:main]
|
||||||
use = egg:gunicorn#main
|
use = egg:gunicorn#main
|
||||||
host = 127.0.0.1
|
host = 127.0.0.1:8000, 127.0.0.1:8001
|
||||||
port = 5000
|
|
||||||
workers = 2
|
workers = 2
|
||||||
|
|
||||||
[app:main]
|
[app:main]
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class PasterBaseApplication(Application):
|
|||||||
if host and port:
|
if host and port:
|
||||||
cfg['bind'] = '%s:%s' % (host, port)
|
cfg['bind'] = '%s:%s' % (host, port)
|
||||||
elif host:
|
elif host:
|
||||||
cfg['bind'] = host
|
cfg['bind'] = host.split(',')
|
||||||
|
|
||||||
cfg['workers'] = int(lc.get('workers', 1))
|
cfg['workers'] = int(lc.get('workers', 1))
|
||||||
cfg['umask'] = int(lc.get('umask', 0))
|
cfg['umask'] = int(lc.get('umask', 0))
|
||||||
@ -55,19 +55,11 @@ class PasterBaseApplication(Application):
|
|||||||
parser = ConfigParser.ConfigParser()
|
parser = ConfigParser.ConfigParser()
|
||||||
parser.read([self.cfgfname])
|
parser.read([self.cfgfname])
|
||||||
if parser.has_section('loggers'):
|
if parser.has_section('loggers'):
|
||||||
if sys.version_info >= (2, 6):
|
from logging.config import fileConfig
|
||||||
from logging.config import fileConfig
|
|
||||||
else:
|
|
||||||
# Use our custom fileConfig -- 2.5.1's with a custom Formatter class
|
|
||||||
# and less strict whitespace (which were incorporated into 2.6's)
|
|
||||||
from gunicorn.logging_config import fileConfig
|
|
||||||
|
|
||||||
config_file = os.path.abspath(self.cfgfname)
|
config_file = os.path.abspath(self.cfgfname)
|
||||||
fileConfig(config_file, dict(__file__=config_file,
|
fileConfig(config_file, dict(__file__=config_file,
|
||||||
here=os.path.dirname(config_file)))
|
here=os.path.dirname(config_file)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PasterApplication(PasterBaseApplication):
|
class PasterApplication(PasterBaseApplication):
|
||||||
|
|
||||||
def init(self, parser, opts, args):
|
def init(self, parser, opts, args):
|
||||||
@ -111,7 +103,7 @@ class PasterServerApplication(PasterBaseApplication):
|
|||||||
bind = "%s:%s" % (host, port)
|
bind = "%s:%s" % (host, port)
|
||||||
else:
|
else:
|
||||||
bind = host
|
bind = host
|
||||||
cfg["bind"] = bind
|
cfg["bind"] = bind.split(',')
|
||||||
|
|
||||||
if gcfg:
|
if gcfg:
|
||||||
for k, v in gcfg.items():
|
for k, v in gcfg.items():
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import traceback
|
|||||||
|
|
||||||
from gunicorn.errors import HaltServer
|
from gunicorn.errors import HaltServer
|
||||||
from gunicorn.pidfile import Pidfile
|
from gunicorn.pidfile import Pidfile
|
||||||
from gunicorn.sock import create_socket
|
from gunicorn.sock import create_sockets
|
||||||
from gunicorn import util
|
from gunicorn import util
|
||||||
|
|
||||||
from gunicorn import __version__, SERVER_SOFTWARE
|
from gunicorn import __version__, SERVER_SOFTWARE
|
||||||
@ -35,7 +35,7 @@ class Arbiter(object):
|
|||||||
|
|
||||||
START_CTX = {}
|
START_CTX = {}
|
||||||
|
|
||||||
LISTENER = None
|
LISTENERS = []
|
||||||
WORKERS = {}
|
WORKERS = {}
|
||||||
PIPE = []
|
PIPE = []
|
||||||
|
|
||||||
@ -120,12 +120,12 @@ class Arbiter(object):
|
|||||||
self.pidfile.create(self.pid)
|
self.pidfile.create(self.pid)
|
||||||
self.cfg.on_starting(self)
|
self.cfg.on_starting(self)
|
||||||
self.init_signals()
|
self.init_signals()
|
||||||
if not self.LISTENER:
|
if not self.LISTENERS:
|
||||||
self.LISTENER = create_socket(self.cfg, self.log)
|
self.LISTENERS = create_sockets(self.cfg, self.log)
|
||||||
|
|
||||||
|
listeners_str = ",".join([str(l) for l in self.LISTENERS])
|
||||||
self.log.debug("Arbiter booted")
|
self.log.debug("Arbiter booted")
|
||||||
self.log.info("Listening at: %s (%s)", self.LISTENER,
|
self.log.info("Listening at: %s (%s)", listeners_str, self.pid)
|
||||||
self.pid)
|
|
||||||
self.log.info("Using worker: %s",
|
self.log.info("Using worker: %s",
|
||||||
self.cfg.settings['worker_class'].get())
|
self.cfg.settings['worker_class'].get())
|
||||||
|
|
||||||
@ -321,11 +321,13 @@ class Arbiter(object):
|
|||||||
:attr graceful: boolean, If True (the default) workers will be
|
:attr graceful: boolean, If True (the default) workers will be
|
||||||
killed gracefully (ie. trying to wait for the current connection)
|
killed gracefully (ie. trying to wait for the current connection)
|
||||||
"""
|
"""
|
||||||
try:
|
for l in self.LISTENERS:
|
||||||
self.LISTENER.close()
|
try:
|
||||||
except Exception:
|
l.close()
|
||||||
pass
|
except Exception:
|
||||||
self.LISTENER = None
|
pass
|
||||||
|
|
||||||
|
self.LISTENERS = []
|
||||||
sig = signal.SIGQUIT
|
sig = signal.SIGQUIT
|
||||||
if not graceful:
|
if not graceful:
|
||||||
sig = signal.SIGTERM
|
sig = signal.SIGTERM
|
||||||
@ -348,11 +350,16 @@ class Arbiter(object):
|
|||||||
self.master_name = "Old Master"
|
self.master_name = "Old Master"
|
||||||
return
|
return
|
||||||
|
|
||||||
os.environ['GUNICORN_FD'] = str(self.LISTENER.fileno())
|
fds = [l.fileno() for l in self.LISTENERS]
|
||||||
|
os.environ['GUNICORN_FD'] = ",".join([str(fd) for fd in fds])
|
||||||
|
|
||||||
os.chdir(self.START_CTX['cwd'])
|
os.chdir(self.START_CTX['cwd'])
|
||||||
self.cfg.pre_exec(self)
|
self.cfg.pre_exec(self)
|
||||||
util.closerange(3, self.LISTENER.fileno())
|
|
||||||
util.closerange(self.LISTENER.fileno()+1, util.get_maxfd())
|
# close all file descriptors except bound sockets
|
||||||
|
util.closerange(3, fds[0])
|
||||||
|
util.closerange(fds[-1]+1, util.get_maxfd())
|
||||||
|
|
||||||
os.execvpe(self.START_CTX[0], self.START_CTX['args'], os.environ)
|
os.execvpe(self.START_CTX[0], self.START_CTX['args'], os.environ)
|
||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
@ -367,9 +374,11 @@ class Arbiter(object):
|
|||||||
|
|
||||||
# do we need to change listener ?
|
# do we need to change listener ?
|
||||||
if old_address != self.cfg.address:
|
if old_address != self.cfg.address:
|
||||||
self.LISTENER.close()
|
# close all listeners
|
||||||
self.LISTENER = create_socket(self.cfg, self.log)
|
[l.close for l in self.LISTENERS]
|
||||||
self.log.info("Listening at: %s", self.LISTENER)
|
# init new listeners
|
||||||
|
self.LISTENERS = create_sockeSt(self.cfg, self.log)
|
||||||
|
self.log.info("Listening at: %s", ",".join(str(self.LISTENERS)))
|
||||||
|
|
||||||
# do some actions on reload
|
# do some actions on reload
|
||||||
self.cfg.on_reload(self)
|
self.cfg.on_reload(self)
|
||||||
@ -451,7 +460,7 @@ class Arbiter(object):
|
|||||||
|
|
||||||
def spawn_worker(self):
|
def spawn_worker(self):
|
||||||
self.worker_age += 1
|
self.worker_age += 1
|
||||||
worker = self.worker_class(self.worker_age, self.pid, self.LISTENER,
|
worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS,
|
||||||
self.app, self.timeout/2.0,
|
self.app, self.timeout/2.0,
|
||||||
self.cfg, self.log)
|
self.cfg, self.log)
|
||||||
self.cfg.pre_fork(self, worker)
|
self.cfg.pre_fork(self, worker)
|
||||||
|
|||||||
@ -86,8 +86,8 @@ class Config(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def address(self):
|
def address(self):
|
||||||
bind = self.settings['bind'].get()
|
s = self.settings['bind'].get()
|
||||||
return util.parse_address(bytes_to_str(bind))
|
return [util.parse_address(bytes_to_str(bind)) for bind in s]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def uid(self):
|
def uid(self):
|
||||||
@ -220,6 +220,16 @@ def validate_string(val):
|
|||||||
raise TypeError("Not a string: %s" % val)
|
raise TypeError("Not a string: %s" % val)
|
||||||
return val.strip()
|
return val.strip()
|
||||||
|
|
||||||
|
def validate_list_string(val):
|
||||||
|
if not val:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# legacy syntax
|
||||||
|
if isinstance(val, string_types):
|
||||||
|
val = [val]
|
||||||
|
|
||||||
|
return [validate_string(v) for v in val]
|
||||||
|
|
||||||
def validate_string_to_list(val):
|
def validate_string_to_list(val):
|
||||||
val = validate_string(val)
|
val = validate_string(val)
|
||||||
|
|
||||||
@ -315,15 +325,16 @@ class ConfigFile(Setting):
|
|||||||
|
|
||||||
class Bind(Setting):
|
class Bind(Setting):
|
||||||
name = "bind"
|
name = "bind"
|
||||||
|
action = "append"
|
||||||
section = "Server Socket"
|
section = "Server Socket"
|
||||||
cli = ["-b", "--bind"]
|
cli = ["-b", "--bind"]
|
||||||
meta = "ADDRESS"
|
meta = "ADDRESS"
|
||||||
validator = validate_string
|
validator = validate_list_string
|
||||||
|
|
||||||
if 'PORT' in os.environ:
|
if 'PORT' in os.environ:
|
||||||
default = '0.0.0.0:{0}'.format(os.environ.get('PORT'))
|
default = ['0.0.0.0:{0}'.format(os.environ.get('PORT'))]
|
||||||
else:
|
else:
|
||||||
default = '127.0.0.1:8000'
|
default = ['127.0.0.1:8000']
|
||||||
|
|
||||||
desc = """\
|
desc = """\
|
||||||
The socket to bind.
|
The socket to bind.
|
||||||
|
|||||||
110
gunicorn/sock.py
110
gunicorn/sock.py
@ -14,10 +14,11 @@ from gunicorn.six import string_types
|
|||||||
|
|
||||||
class BaseSocket(object):
|
class BaseSocket(object):
|
||||||
|
|
||||||
def __init__(self, conf, log, fd=None):
|
def __init__(self, address, conf, log, fd=None):
|
||||||
self.log = log
|
self.log = log
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
self.address = conf.address
|
|
||||||
|
self.cfg_addr = address
|
||||||
if fd is None:
|
if fd is None:
|
||||||
sock = socket.socket(self.FAMILY, socket.SOCK_STREAM)
|
sock = socket.socket(self.FAMILY, socket.SOCK_STREAM)
|
||||||
else:
|
else:
|
||||||
@ -39,7 +40,7 @@ class BaseSocket(object):
|
|||||||
return sock
|
return sock
|
||||||
|
|
||||||
def bind(self, sock):
|
def bind(self, sock):
|
||||||
sock.bind(self.address)
|
sock.bind(self.cfg_addr)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
try:
|
try:
|
||||||
@ -72,37 +73,28 @@ class UnixSocket(BaseSocket):
|
|||||||
|
|
||||||
FAMILY = socket.AF_UNIX
|
FAMILY = socket.AF_UNIX
|
||||||
|
|
||||||
def __init__(self, conf, log, fd=None):
|
def __init__(self, address, log, fd=None):
|
||||||
if fd is None:
|
if fd is None:
|
||||||
try:
|
try:
|
||||||
os.remove(conf.address)
|
os.remove(address)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
super(UnixSocket, self).__init__(conf, log, fd=fd)
|
super(UnixSocket, self).__init__(addr, log, fd=fd)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "unix:%s" % self.address
|
return "unix:%s" % self.cfg_addr
|
||||||
|
|
||||||
def bind(self, sock):
|
def bind(self, sock):
|
||||||
old_umask = os.umask(self.conf.umask)
|
old_umask = os.umask(self.conf.umask)
|
||||||
sock.bind(self.address)
|
sock.bind(self.cfg_addr)
|
||||||
util.chown(self.address, self.conf.uid, self.conf.gid)
|
util.chown(self.cfg_addr, self.conf.uid, self.conf.gid)
|
||||||
os.umask(old_umask)
|
os.umask(old_umask)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
super(UnixSocket, self).close()
|
super(UnixSocket, self).close()
|
||||||
os.unlink(self.address)
|
os.unlink(self.cfg_addr)
|
||||||
|
|
||||||
def create_socket(conf, log):
|
|
||||||
"""
|
|
||||||
Create a new socket for the given address. If the
|
|
||||||
address is a tuple, a TCP socket is created. If it
|
|
||||||
is a string, a Unix socket is created. Otherwise
|
|
||||||
a TypeError is raised.
|
|
||||||
"""
|
|
||||||
# get it only once
|
|
||||||
addr = conf.address
|
|
||||||
|
|
||||||
|
def _sock_type(addr):
|
||||||
if isinstance(addr, tuple):
|
if isinstance(addr, tuple):
|
||||||
if util.is_ipv6(addr[0]):
|
if util.is_ipv6(addr[0]):
|
||||||
sock_type = TCP6Socket
|
sock_type = TCP6Socket
|
||||||
@ -112,33 +104,63 @@ def create_socket(conf, log):
|
|||||||
sock_type = UnixSocket
|
sock_type = UnixSocket
|
||||||
else:
|
else:
|
||||||
raise TypeError("Unable to create socket from: %r" % addr)
|
raise TypeError("Unable to create socket from: %r" % addr)
|
||||||
|
return sock_type
|
||||||
|
|
||||||
|
def create_sockets(conf, log):
|
||||||
|
"""
|
||||||
|
Create a new socket for the given address. If the
|
||||||
|
address is a tuple, a TCP socket is created. If it
|
||||||
|
is a string, a Unix socket is created. Otherwise
|
||||||
|
a TypeError is raised.
|
||||||
|
"""
|
||||||
|
# get it only once
|
||||||
|
laddr = conf.address
|
||||||
|
listeners = []
|
||||||
|
|
||||||
|
# sockets are already bound
|
||||||
if 'GUNICORN_FD' in os.environ:
|
if 'GUNICORN_FD' in os.environ:
|
||||||
fd = int(os.environ.pop('GUNICORN_FD'))
|
fds = os.environ.pop('GUNICORN_FD').split(',')
|
||||||
try:
|
for i, fd in enumerate(fds):
|
||||||
return sock_type(conf, log, fd=fd)
|
fd = int(fd)
|
||||||
except socket.error as e:
|
addr = laddr[i]
|
||||||
if e.args[0] == errno.ENOTCONN:
|
sock_type = _sock_type(addr)
|
||||||
log.error("GUNICORN_FD should refer to an open socket.")
|
|
||||||
|
try:
|
||||||
|
listeners.append(sock_type(addr, conf, log, fd=fd))
|
||||||
|
except socket.error as e:
|
||||||
|
if e.args[0] == errno.ENOTCONN:
|
||||||
|
log.error("GUNICORN_FD should refer to an open socket.")
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return listeners
|
||||||
|
|
||||||
|
# no sockets is bound, first initialization of gunicorn in this env.
|
||||||
|
for addr in laddr:
|
||||||
|
sock_type = _sock_type(addr)
|
||||||
|
|
||||||
|
# If we fail to create a socket from GUNICORN_FD
|
||||||
|
# we fall through and try and open the socket
|
||||||
|
# normally.
|
||||||
|
sock = None
|
||||||
|
for i in range(5):
|
||||||
|
try:
|
||||||
|
sock = sock_type(addr, conf, log)
|
||||||
|
except socket.error as e:
|
||||||
|
if e.args[0] == errno.EADDRINUSE:
|
||||||
|
log.error("Connection in use: %s", str(addr))
|
||||||
|
if e.args[0] == errno.EADDRNOTAVAIL:
|
||||||
|
log.error("Invalid address: %s", str(addr))
|
||||||
|
sys.exit(1)
|
||||||
|
if i < 5:
|
||||||
|
log.error("Retrying in 1 second.")
|
||||||
|
time.sleep(1)
|
||||||
else:
|
else:
|
||||||
raise
|
break
|
||||||
|
|
||||||
# If we fail to create a socket from GUNICORN_FD
|
if sock is None:
|
||||||
# we fall through and try and open the socket
|
log.error("Can't connect to %s", str(addr))
|
||||||
# normally.
|
sys.exit(1)
|
||||||
|
|
||||||
for i in range(5):
|
listeners.append(sock)
|
||||||
try:
|
|
||||||
return sock_type(conf, log)
|
|
||||||
except socket.error as e:
|
|
||||||
if e.args[0] == errno.EADDRINUSE:
|
|
||||||
log.error("Connection in use: %s", str(addr))
|
|
||||||
if e.args[0] == errno.EADDRNOTAVAIL:
|
|
||||||
log.error("Invalid address: %s", str(addr))
|
|
||||||
sys.exit(1)
|
|
||||||
if i < 5:
|
|
||||||
log.error("Retrying in 1 second.")
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
log.error("Can't connect to %s", str(addr))
|
return listeners
|
||||||
sys.exit(1)
|
|
||||||
|
|||||||
@ -26,14 +26,14 @@ class AsyncWorker(base.Worker):
|
|||||||
def timeout_ctx(self):
|
def timeout_ctx(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def handle(self, client, addr):
|
def handle(self, listener, client, addr):
|
||||||
req = None
|
req = None
|
||||||
try:
|
try:
|
||||||
parser = http.RequestParser(self.cfg, client)
|
parser = http.RequestParser(self.cfg, client)
|
||||||
try:
|
try:
|
||||||
if not self.cfg.keepalive:
|
if not self.cfg.keepalive:
|
||||||
req = six.next(parser)
|
req = six.next(parser)
|
||||||
self.handle_request(req, client, addr)
|
self.handle_request(listener, req, client, addr)
|
||||||
else:
|
else:
|
||||||
# keepalive loop
|
# keepalive loop
|
||||||
while True:
|
while True:
|
||||||
@ -42,7 +42,7 @@ class AsyncWorker(base.Worker):
|
|||||||
req = six.next(parser)
|
req = six.next(parser)
|
||||||
if not req:
|
if not req:
|
||||||
break
|
break
|
||||||
self.handle_request(req, client, addr)
|
self.handle_request(listener, req, client, addr)
|
||||||
except http.errors.NoMoreData as e:
|
except http.errors.NoMoreData as e:
|
||||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
@ -64,11 +64,12 @@ class AsyncWorker(base.Worker):
|
|||||||
finally:
|
finally:
|
||||||
util.close(client)
|
util.close(client)
|
||||||
|
|
||||||
def handle_request(self, req, sock, addr):
|
def handle_request(self, listener, req, sock, addr):
|
||||||
request_start = datetime.now()
|
request_start = datetime.now()
|
||||||
try:
|
try:
|
||||||
self.cfg.pre_request(self, req)
|
self.cfg.pre_request(self, req)
|
||||||
resp, environ = wsgi.create(req, sock, addr, self.address, self.cfg)
|
resp, environ = wsgi.create(req, sock, addr,
|
||||||
|
listener.getsockname(), self.cfg)
|
||||||
self.nr += 1
|
self.nr += 1
|
||||||
if self.alive and self.nr >= self.max_requests:
|
if self.alive and self.nr >= self.max_requests:
|
||||||
self.log.info("Autorestarting worker after current request.")
|
self.log.info("Autorestarting worker after current request.")
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class Worker(object):
|
|||||||
|
|
||||||
PIPE = []
|
PIPE = []
|
||||||
|
|
||||||
def __init__(self, age, ppid, socket, app, timeout, cfg, log):
|
def __init__(self, age, ppid, sockets, app, timeout, cfg, log):
|
||||||
"""\
|
"""\
|
||||||
This is called pre-fork so it shouldn't do anything to the
|
This is called pre-fork so it shouldn't do anything to the
|
||||||
current process. If there's a need to make process wide
|
current process. If there's a need to make process wide
|
||||||
@ -34,7 +34,7 @@ class Worker(object):
|
|||||||
"""
|
"""
|
||||||
self.age = age
|
self.age = age
|
||||||
self.ppid = ppid
|
self.ppid = ppid
|
||||||
self.socket = socket
|
self.sockets = sockets
|
||||||
self.app = app
|
self.app = app
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.cfg = cfg
|
self.cfg = cfg
|
||||||
@ -45,7 +45,6 @@ class Worker(object):
|
|||||||
self.alive = True
|
self.alive = True
|
||||||
self.log = log
|
self.log = log
|
||||||
self.debug = cfg.debug
|
self.debug = cfg.debug
|
||||||
self.address = self.socket.getsockname()
|
|
||||||
self.tmp = WorkerTmp(cfg)
|
self.tmp = WorkerTmp(cfg)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -90,7 +89,7 @@ class Worker(object):
|
|||||||
util.close_on_exec(p)
|
util.close_on_exec(p)
|
||||||
|
|
||||||
# Prevent fd inherientence
|
# Prevent fd inherientence
|
||||||
util.close_on_exec(self.socket)
|
[util.close_on_exec(s) for s in self.sockets]
|
||||||
util.close_on_exec(self.tmp.fileno())
|
util.close_on_exec(self.tmp.fileno())
|
||||||
|
|
||||||
self.log.close_on_exec()
|
self.log.close_on_exec()
|
||||||
|
|||||||
@ -3,10 +3,9 @@
|
|||||||
# 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 __future__ import with_statement
|
from functools import partial
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import eventlet
|
import eventlet
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -33,10 +32,13 @@ class EventletWorker(AsyncWorker):
|
|||||||
return eventlet.Timeout(self.cfg.keepalive or None, False)
|
return eventlet.Timeout(self.cfg.keepalive or None, False)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.socket = GreenSocket(family_or_realsock=self.socket.sock)
|
acceptors = []
|
||||||
self.socket.setblocking(1)
|
for sock in self.sockets:
|
||||||
self.acceptor = eventlet.spawn(eventlet.serve, self.socket,
|
s = GreenSocket(family_or_realsock=sock)
|
||||||
self.handle, self.worker_connections)
|
s.setblocking(1)
|
||||||
|
hfun = partial(self.handle, s)
|
||||||
|
acceptor = eventlet.spawn(eventlet.serve, s, hfun,
|
||||||
|
self.worker_connections)
|
||||||
|
|
||||||
while self.alive:
|
while self.alive:
|
||||||
self.notify()
|
self.notify()
|
||||||
@ -48,4 +50,4 @@ class EventletWorker(AsyncWorker):
|
|||||||
|
|
||||||
self.notify()
|
self.notify()
|
||||||
with eventlet.Timeout(self.cfg.graceful_timeout, False):
|
with eventlet.Timeout(self.cfg.graceful_timeout, False):
|
||||||
eventlet.kill(self.acceptor, eventlet.StopServe)
|
[eventlet.kill(a, eventlet.StopServe) for a in acceptors]
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from __future__ import with_statement
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from functools import partial
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# workaround on osx, disable kqueue
|
# workaround on osx, disable kqueue
|
||||||
@ -51,18 +52,23 @@ class GeventWorker(AsyncWorker):
|
|||||||
def timeout_ctx(self):
|
def timeout_ctx(self):
|
||||||
return gevent.Timeout(self.cfg.keepalive, False)
|
return gevent.Timeout(self.cfg.keepalive, False)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.socket.setblocking(1)
|
servers = []
|
||||||
|
for s in self.sockets:
|
||||||
|
s.setblocking(1)
|
||||||
|
pool = Pool(self.worker_connections)
|
||||||
|
if self.server_class is not None:
|
||||||
|
server = self.server_class(
|
||||||
|
s, application=self.wsgi, spawn=pool, log=self.log,
|
||||||
|
handler_class=self.wsgi_handler)
|
||||||
|
else:
|
||||||
|
hfun = partial(self.handle, s)
|
||||||
|
server = StreamServer(s, handle=hfun, spawn=pool)
|
||||||
|
|
||||||
pool = Pool(self.worker_connections)
|
server.start()
|
||||||
if self.server_class is not None:
|
servers.append(server)
|
||||||
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()
|
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
try:
|
try:
|
||||||
while self.alive:
|
while self.alive:
|
||||||
@ -79,13 +85,19 @@ class GeventWorker(AsyncWorker):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Stop accepting requests
|
# Stop accepting requests
|
||||||
server.kill()
|
[server.stop_accepting() for server in servers]
|
||||||
|
|
||||||
# Handle current requests until graceful_timeout
|
# Handle current requests until graceful_timeout
|
||||||
ts = time.time()
|
ts = time.time()
|
||||||
while time.time() - ts <= self.cfg.graceful_timeout:
|
while time.time() - ts <= self.cfg.graceful_timeout:
|
||||||
if server.pool.free_count() == server.pool.size:
|
accepting = 0
|
||||||
return # all requests was handled
|
for server in servers:
|
||||||
|
if server.pool.free_count() != server.pool.size:
|
||||||
|
accepting += 1
|
||||||
|
|
||||||
|
# if no server is accepting a connection, we can exit
|
||||||
|
if not accepting:
|
||||||
|
return
|
||||||
|
|
||||||
self.notify()
|
self.notify()
|
||||||
gevent.sleep(1.0)
|
gevent.sleep(1.0)
|
||||||
|
|||||||
@ -50,7 +50,6 @@ class TornadoWorker(Worker):
|
|||||||
self.ioloop.stop()
|
self.ioloop.stop()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.socket.setblocking(0)
|
|
||||||
self.ioloop = IOLoop.instance()
|
self.ioloop = IOLoop.instance()
|
||||||
self.alive = True
|
self.alive = True
|
||||||
PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start()
|
PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start()
|
||||||
@ -77,16 +76,15 @@ class TornadoWorker(Worker):
|
|||||||
sys.modules["tornado.httpserver"] = httpserver
|
sys.modules["tornado.httpserver"] = httpserver
|
||||||
|
|
||||||
server = tornado.httpserver.HTTPServer(app, io_loop=self.ioloop)
|
server = tornado.httpserver.HTTPServer(app, io_loop=self.ioloop)
|
||||||
if hasattr(server, "add_socket"): # tornado > 2.0
|
for s in self.sockets:
|
||||||
server.add_socket(self.socket)
|
s.setblocking(0)
|
||||||
elif hasattr(server, "_sockets"): # tornado 2.0
|
if hasattr(server, "add_socket"): # tornado > 2.0
|
||||||
server._sockets[self.socket.fileno()] = self.socket
|
server.add_socket(s)
|
||||||
else: # tornado 1.2 or earlier
|
elif hasattr(server, "_sockets"): # tornado 2.0
|
||||||
server._socket = self.socket
|
server._sockets[s.fileno()] = s
|
||||||
|
|
||||||
server.no_keep_alive = self.cfg.keepalive <= 0
|
server.no_keep_alive = self.cfg.keepalive <= 0
|
||||||
server.xheaders = bool(self.cfg.x_forwarded_for_header)
|
server.xheaders = bool(self.cfg.x_forwarded_for_header)
|
||||||
|
|
||||||
server.start(num_processes=1)
|
server.start(num_processes=1)
|
||||||
|
|
||||||
self.ioloop.start()
|
self.ioloop.start()
|
||||||
|
|||||||
@ -21,8 +21,9 @@ class SyncWorker(base.Worker):
|
|||||||
def run(self):
|
def run(self):
|
||||||
# self.socket appears to lose its blocking status after
|
# self.socket appears to lose its blocking status after
|
||||||
# we fork in the arbiter. Reset it here.
|
# we fork in the arbiter. Reset it here.
|
||||||
self.socket.setblocking(0)
|
[s.setblocking(0) for s in self.sockets]
|
||||||
|
|
||||||
|
ready = self.sockets
|
||||||
while self.alive:
|
while self.alive:
|
||||||
self.notify()
|
self.notify()
|
||||||
|
|
||||||
@ -30,20 +31,23 @@ class SyncWorker(base.Worker):
|
|||||||
# that no connection is waiting we fall down to the
|
# that no connection is waiting we fall down to the
|
||||||
# select which is where we'll wait for a bit for new
|
# select which is where we'll wait for a bit for new
|
||||||
# workers to come give us some love.
|
# workers to come give us some love.
|
||||||
try:
|
|
||||||
client, addr = self.socket.accept()
|
|
||||||
client.setblocking(1)
|
|
||||||
util.close_on_exec(client)
|
|
||||||
self.handle(client, addr)
|
|
||||||
|
|
||||||
# Keep processing clients until no one is waiting. This
|
for sock in ready:
|
||||||
# prevents the need to select() for every client that we
|
try:
|
||||||
# process.
|
client, addr = sock.accept()
|
||||||
continue
|
client.setblocking(1)
|
||||||
|
util.close_on_exec(client)
|
||||||
|
self.handle(sock, client, addr)
|
||||||
|
|
||||||
except socket.error as e:
|
# Keep processing clients until no one is waiting. This
|
||||||
if e.args[0] not in (errno.EAGAIN, errno.ECONNABORTED):
|
# prevents the need to select() for every client that we
|
||||||
raise
|
# process.
|
||||||
|
continue
|
||||||
|
|
||||||
|
except socket.error as e:
|
||||||
|
if e.args[0] not in (errno.EAGAIN, errno.ECONNABORTED,
|
||||||
|
errno.EWOULDBLOCK):
|
||||||
|
raise
|
||||||
|
|
||||||
# If our parent changed then we shut down.
|
# If our parent changed then we shut down.
|
||||||
if self.ppid != os.getppid():
|
if self.ppid != os.getppid():
|
||||||
@ -52,25 +56,28 @@ class SyncWorker(base.Worker):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.notify()
|
self.notify()
|
||||||
ret = select.select([self.socket], [], self.PIPE, self.timeout)
|
ret = select.select(self.sockets, [], self.PIPE, self.timeout)
|
||||||
if ret[0]:
|
if ret[0]:
|
||||||
|
ready = ret[0]
|
||||||
continue
|
continue
|
||||||
except select.error as e:
|
except select.error as e:
|
||||||
if e.args[0] == errno.EINTR:
|
if e.args[0] == errno.EINTR:
|
||||||
|
ready = self.sockets
|
||||||
continue
|
continue
|
||||||
if e.args[0] == errno.EBADF:
|
if e.args[0] == errno.EBADF:
|
||||||
if self.nr < 0:
|
if self.nr < 0:
|
||||||
|
ready = self.sockets
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def handle(self, client, addr):
|
def handle(self, listener, client, addr):
|
||||||
req = None
|
req = None
|
||||||
try:
|
try:
|
||||||
parser = http.RequestParser(self.cfg, client)
|
parser = http.RequestParser(self.cfg, client)
|
||||||
req = six.next(parser)
|
req = six.next(parser)
|
||||||
self.handle_request(req, client, addr)
|
self.handle_request(listener, req, client, addr)
|
||||||
except http.errors.NoMoreData as e:
|
except http.errors.NoMoreData as e:
|
||||||
self.log.debug("Ignored premature client disconnection. %s", e)
|
self.log.debug("Ignored premature client disconnection. %s", e)
|
||||||
except StopIteration as e:
|
except StopIteration as e:
|
||||||
@ -85,13 +92,13 @@ class SyncWorker(base.Worker):
|
|||||||
finally:
|
finally:
|
||||||
util.close(client)
|
util.close(client)
|
||||||
|
|
||||||
def handle_request(self, req, client, addr):
|
def handle_request(self, listener, req, client, addr):
|
||||||
environ = {}
|
environ = {}
|
||||||
try:
|
try:
|
||||||
self.cfg.pre_request(self, req)
|
self.cfg.pre_request(self, req)
|
||||||
request_start = datetime.now()
|
request_start = datetime.now()
|
||||||
resp, environ = wsgi.create(req, client, addr,
|
resp, environ = wsgi.create(req, client, addr,
|
||||||
self.address, self.cfg)
|
listener.getsockname(), self.cfg)
|
||||||
# Force the connection closed until someone shows
|
# Force the connection closed until someone shows
|
||||||
# a buffering proxy that supports Keep-Alive to
|
# a buffering proxy that supports Keep-Alive to
|
||||||
# the backend.
|
# the backend.
|
||||||
@ -124,4 +131,3 @@ class SyncWorker(base.Worker):
|
|||||||
self.cfg.post_request(self, req, environ)
|
self.cfg.post_request(self, req, environ)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@ -63,7 +63,7 @@ def test_property_access():
|
|||||||
t.eq(c.workers, 3)
|
t.eq(c.workers, 3)
|
||||||
|
|
||||||
# Address is parsed
|
# Address is parsed
|
||||||
t.eq(c.address, ("127.0.0.1", 8000))
|
t.eq(c.address, [("127.0.0.1", 8000)])
|
||||||
|
|
||||||
# User and group defaults
|
# User and group defaults
|
||||||
t.eq(os.geteuid(), c.uid)
|
t.eq(os.geteuid(), c.uid)
|
||||||
@ -165,7 +165,7 @@ def test_callable_validation_for_string():
|
|||||||
def test_cmd_line():
|
def test_cmd_line():
|
||||||
with AltArgs(["prog_name", "-b", "blargh"]):
|
with AltArgs(["prog_name", "-b", "blargh"]):
|
||||||
app = NoConfigApp()
|
app = NoConfigApp()
|
||||||
t.eq(app.cfg.bind, "blargh")
|
t.eq(app.cfg.bind, ["blargh"])
|
||||||
with AltArgs(["prog_name", "-w", "3"]):
|
with AltArgs(["prog_name", "-w", "3"]):
|
||||||
app = NoConfigApp()
|
app = NoConfigApp()
|
||||||
t.eq(app.cfg.workers, 3)
|
t.eq(app.cfg.workers, 3)
|
||||||
@ -183,12 +183,12 @@ def test_app_config():
|
|||||||
def test_load_config():
|
def test_load_config():
|
||||||
with AltArgs(["prog_name", "-c", cfg_file()]):
|
with AltArgs(["prog_name", "-c", cfg_file()]):
|
||||||
app = NoConfigApp()
|
app = NoConfigApp()
|
||||||
t.eq(app.cfg.bind, "unix:/tmp/bar/baz")
|
t.eq(app.cfg.bind, ["unix:/tmp/bar/baz"])
|
||||||
t.eq(app.cfg.workers, 3)
|
t.eq(app.cfg.workers, 3)
|
||||||
t.eq(app.cfg.proc_name, "fooey")
|
t.eq(app.cfg.proc_name, "fooey")
|
||||||
|
|
||||||
def test_cli_overrides_config():
|
def test_cli_overrides_config():
|
||||||
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]):
|
with AltArgs(["prog_name", "-c", cfg_file(), "-b", "blarney"]):
|
||||||
app = NoConfigApp()
|
app = NoConfigApp()
|
||||||
t.eq(app.cfg.bind, "blarney")
|
t.eq(app.cfg.bind, ["blarney"])
|
||||||
t.eq(app.cfg.proc_name, "fooey")
|
t.eq(app.cfg.proc_name, "fooey")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user