fix USR2/HUP handling. spotted by @jbergstroem on irc, thanks !

This commit is contained in:
Benoit Chesneau 2010-02-08 18:14:12 +01:00
parent cc060fbd0e
commit 652f8d2734
2 changed files with 62 additions and 26 deletions

View File

@ -5,6 +5,7 @@
from __future__ import with_statement from __future__ import with_statement
import copy
import errno import errno
import fcntl import fcntl
import logging import logging
@ -15,12 +16,15 @@ import socket
import sys import sys
import tempfile import tempfile
import time import time
import traceback
from gunicorn.worker import Worker from gunicorn.worker import Worker
from gunicorn import util from gunicorn import util
class Arbiter(object): class Arbiter(object):
START_CTX = {}
LISTENER = None LISTENER = None
WORKERS = {} WORKERS = {}
PIPE = [] PIPE = []
@ -47,7 +51,26 @@ class Arbiter(object):
self.log = logging.getLogger(__name__) self.log = logging.getLogger(__name__)
self.opts = kwargs self.opts = kwargs
self._pidfile = None self._pidfile = None
self.master_name = "Master"
# get current path, try to use PWD env first
try:
a = os.stat(os.environ('PWD'))
b = os.stat(os.getcwd())
if a.ino == b.ino and a.dev == b.dev:
cwd = pwd
else:
cwd = os.getcwd()
except:
cwd = os.getcwd()
# init start context
self.START_CTX = {
"argv": copy.copy(sys.argv),
"cwd": cwd,
0: copy.copy(sys.argv[0])
}
def start(self): def start(self):
self.pid = os.getpid() self.pid = os.getpid()
@ -140,18 +163,18 @@ class Arbiter(object):
self.log.error("should be a non GUNICORN environnement") self.log.error("should be a non GUNICORN environnement")
else: else:
raise raise
else:
for i in range(5): for i in range(5):
try: try:
sock = self.init_socket(addr) sock = self.init_socket(addr)
self.LISTENER = sock self.LISTENER = sock
break break
except socket.error, e: except socket.error, e:
if e[0] == errno.EADDRINUSE: if e[0] == errno.EADDRINUSE:
self.log.error("Connection in use: %s" % str(addr)) self.log.error("Connection in use: %s" % str(addr))
if i < 5: if i < 5:
self.log.error("Retrying in 1 second.") self.log.error("Retrying in 1 second.")
time.sleep(1) time.sleep(1)
if self.LISTENER: if self.LISTENER:
try: try:
@ -168,7 +191,9 @@ class Arbiter(object):
else: else:
sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
self.set_tcp_sockopts(sock) self.set_tcp_sockopts(sock)
self.set_sockopts(sock, address) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(0)
sock.listen(2048)
return sock return sock
def init_socket(self, address): def init_socket(self, address):
@ -181,7 +206,10 @@ class Arbiter(object):
else: else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_tcp_sockopts(sock) self.set_tcp_sockopts(sock)
self.set_sockopts(sock, address) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(address)
sock.setblocking(0)
sock.listen(2048)
return sock return sock
def set_tcp_sockopts(self, sock): def set_tcp_sockopts(self, sock):
@ -191,12 +219,6 @@ class Arbiter(object):
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NOPUSH, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NOPUSH, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
def set_sockopts(self, sock, address):
sock.setblocking(0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(address)
sock.listen(2048)
def run(self): def run(self):
self.start() self.start()
self.manage_workers() self.manage_workers()
@ -227,14 +249,15 @@ class Arbiter(object):
except KeyboardInterrupt: except KeyboardInterrupt:
break break
except Exception: except Exception:
self.log.info("Unhandled exception in main loop.") self.log.info("Unhandled exception in main loop. [%s]" %
traceback.format_exc())
self.stop(False) self.stop(False)
if self.pidfile: if self.pidfile:
self.unlink_pidfile(self.pidfile) self.unlink_pidfile(self.pidfile)
sys.exit(-1) sys.exit(-1)
self.stop() self.stop()
self.log.info("Master is shutting down.") self.log.info("%s is shutting down." % self.master_name)
if self.pidfile: if self.pidfile:
self.unlink_pidfile(self.pidfile) self.unlink_pidfile(self.pidfile)
sys.exit(0) sys.exit(0)
@ -244,7 +267,7 @@ class Arbiter(object):
self.reap_workers() self.reap_workers()
def handle_hup(self): def handle_hup(self):
self.log.info("Master hang up.") self.log.info("%s hang up." % self.master_name)
self.reexec() self.reexec()
raise StopIteration raise StopIteration
@ -261,10 +284,12 @@ class Arbiter(object):
def handle_ttin(self): def handle_ttin(self):
self.num_workers += 1 self.num_workers += 1
self.manage_workers()
def handle_ttou(self): def handle_ttou(self):
if self.num_workers > 0: if self.num_workers > 0:
self.num_workers -= 1 self.num_workers -= 1
self.manage_workers()
def handle_usr1(self): def handle_usr1(self):
self.kill_workers(signal.SIGUSR1) self.kill_workers(signal.SIGUSR1)
@ -316,10 +341,20 @@ class Arbiter(object):
self.kill_workers(signal.SIGKILL) self.kill_workers(signal.SIGKILL)
def reexec(self): def reexec(self):
if self.pidfile:
old_pidfile = "%s.oldbin" % self.pidfile
self.pidfile = old_pidfile
self.reexec_pid = os.fork() self.reexec_pid = os.fork()
if self.reexec_pid == 0: if self.reexec_pid != 0:
os.environ['GUNICORN_FD'] = str(self.LISTENER.fileno()) self.master_name = "Old Master"
os.execlp(sys.argv[0], *sys.argv) return
os.environ['GUNICORN_FD'] = str(self.LISTENER.fileno())
os.chdir(self.START_CTX['cwd'])
cmd = (self.START_CTX[0],) + tuple(self.START_CTX['argv'])
os.execlp(*cmd)
def murder_workers(self): def murder_workers(self):
for (pid, worker) in list(self.WORKERS.items()): for (pid, worker) in list(self.WORKERS.items()):

View File

@ -28,6 +28,7 @@ weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
monthname = [None, monthname = [None,
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
def get_maxfd(): def get_maxfd():
maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]