arbiter.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. # -*- coding: utf-8 -
  2. #
  3. # This file is part of gunicorn released under the MIT license.
  4. # See the NOTICE for more information.
  5. from __future__ import print_function
  6. import errno
  7. import os
  8. import random
  9. import select
  10. import signal
  11. import sys
  12. import time
  13. import traceback
  14. from gunicorn.errors import HaltServer, AppImportError
  15. from gunicorn.pidfile import Pidfile
  16. from gunicorn import sock, systemd, util
  17. from gunicorn import __version__, SERVER_SOFTWARE
  18. class Arbiter(object):
  19. """
  20. Arbiter maintain the workers processes alive. It launches or
  21. kills them if needed. It also manages application reloading
  22. via SIGHUP/USR2.
  23. """
  24. # A flag indicating if a worker failed to
  25. # to boot. If a worker process exist with
  26. # this error code, the arbiter will terminate.
  27. WORKER_BOOT_ERROR = 3
  28. # A flag indicating if an application failed to be loaded
  29. APP_LOAD_ERROR = 4
  30. START_CTX = {}
  31. LISTENERS = []
  32. WORKERS = {}
  33. PIPE = []
  34. # I love dynamic languages
  35. SIG_QUEUE = []
  36. SIGNALS = [getattr(signal, "SIG%s" % x)
  37. for x in "HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()]
  38. SIG_NAMES = dict(
  39. (getattr(signal, name), name[3:].lower()) for name in dir(signal)
  40. if name[:3] == "SIG" and name[3] != "_"
  41. )
  42. def __init__(self, app):
  43. os.environ["SERVER_SOFTWARE"] = SERVER_SOFTWARE
  44. self._num_workers = None
  45. self._last_logged_active_worker_count = None
  46. self.log = None
  47. self.setup(app)
  48. self.pidfile = None
  49. self.systemd = False
  50. self.worker_age = 0
  51. self.reexec_pid = 0
  52. self.master_pid = 0
  53. self.master_name = "Master"
  54. cwd = util.getcwd()
  55. args = sys.argv[:]
  56. args.insert(0, sys.executable)
  57. # init start context
  58. self.START_CTX = {
  59. "args": args,
  60. "cwd": cwd,
  61. 0: sys.executable
  62. }
  63. def _get_num_workers(self):
  64. return self._num_workers
  65. def _set_num_workers(self, value):
  66. old_value = self._num_workers
  67. self._num_workers = value
  68. self.cfg.nworkers_changed(self, value, old_value)
  69. num_workers = property(_get_num_workers, _set_num_workers)
  70. def setup(self, app):
  71. self.app = app
  72. self.cfg = app.cfg
  73. if self.log is None:
  74. self.log = self.cfg.logger_class(app.cfg)
  75. # reopen files
  76. if 'GUNICORN_FD' in os.environ:
  77. self.log.reopen_files()
  78. self.worker_class = self.cfg.worker_class
  79. self.address = self.cfg.address
  80. self.num_workers = self.cfg.workers
  81. self.timeout = self.cfg.timeout
  82. self.proc_name = self.cfg.proc_name
  83. self.log.debug('Current configuration:\n{0}'.format(
  84. '\n'.join(
  85. ' {0}: {1}'.format(config, value.value)
  86. for config, value
  87. in sorted(self.cfg.settings.items(),
  88. key=lambda setting: setting[1]))))
  89. # set enviroment' variables
  90. if self.cfg.env:
  91. for k, v in self.cfg.env.items():
  92. os.environ[k] = v
  93. if self.cfg.preload_app:
  94. self.app.wsgi()
  95. def start(self):
  96. """\
  97. Initialize the arbiter. Start listening and set pidfile if needed.
  98. """
  99. self.log.info("Starting gunicorn %s", __version__)
  100. if 'GUNICORN_PID' in os.environ:
  101. self.master_pid = int(os.environ.get('GUNICORN_PID'))
  102. self.proc_name = self.proc_name + ".2"
  103. self.master_name = "Master.2"
  104. self.pid = os.getpid()
  105. if self.cfg.pidfile is not None:
  106. pidname = self.cfg.pidfile
  107. if self.master_pid != 0:
  108. pidname += ".2"
  109. self.pidfile = Pidfile(pidname)
  110. self.pidfile.create(self.pid)
  111. self.cfg.on_starting(self)
  112. self.init_signals()
  113. if not self.LISTENERS:
  114. fds = None
  115. listen_fds = systemd.listen_fds()
  116. if listen_fds:
  117. self.systemd = True
  118. fds = range(systemd.SD_LISTEN_FDS_START,
  119. systemd.SD_LISTEN_FDS_START + listen_fds)
  120. elif self.master_pid:
  121. fds = []
  122. for fd in os.environ.pop('GUNICORN_FD').split(','):
  123. fds.append(int(fd))
  124. self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds)
  125. listeners_str = ",".join([str(l) for l in self.LISTENERS])
  126. self.log.debug("Arbiter booted")
  127. self.log.info("Listening at: %s (%s)", listeners_str, self.pid)
  128. self.log.info("Using worker: %s", self.cfg.worker_class_str)
  129. # check worker class requirements
  130. if hasattr(self.worker_class, "check_config"):
  131. self.worker_class.check_config(self.cfg, self.log)
  132. self.cfg.when_ready(self)
  133. def init_signals(self):
  134. """\
  135. Initialize master signal handling. Most of the signals
  136. are queued. Child signals only wake up the master.
  137. """
  138. # close old PIPE
  139. if self.PIPE:
  140. [os.close(p) for p in self.PIPE]
  141. # initialize the pipe
  142. self.PIPE = pair = os.pipe()
  143. for p in pair:
  144. util.set_non_blocking(p)
  145. util.close_on_exec(p)
  146. self.log.close_on_exec()
  147. # initialize all signals
  148. [signal.signal(s, self.signal) for s in self.SIGNALS]
  149. signal.signal(signal.SIGCHLD, self.handle_chld)
  150. def signal(self, sig, frame):
  151. if len(self.SIG_QUEUE) < 5:
  152. self.SIG_QUEUE.append(sig)
  153. self.wakeup()
  154. def run(self):
  155. "Main master loop."
  156. self.start()
  157. util._setproctitle("master [%s]" % self.proc_name)
  158. try:
  159. self.manage_workers()
  160. while True:
  161. self.maybe_promote_master()
  162. sig = self.SIG_QUEUE.pop(0) if len(self.SIG_QUEUE) else None
  163. if sig is None:
  164. self.sleep()
  165. self.murder_workers()
  166. self.manage_workers()
  167. continue
  168. if sig not in self.SIG_NAMES:
  169. self.log.info("Ignoring unknown signal: %s", sig)
  170. continue
  171. signame = self.SIG_NAMES.get(sig)
  172. handler = getattr(self, "handle_%s" % signame, None)
  173. if not handler:
  174. self.log.error("Unhandled signal: %s", signame)
  175. continue
  176. self.log.info("Handling signal: %s", signame)
  177. handler()
  178. self.wakeup()
  179. except StopIteration:
  180. self.halt()
  181. except KeyboardInterrupt:
  182. self.halt()
  183. except HaltServer as inst:
  184. self.halt(reason=inst.reason, exit_status=inst.exit_status)
  185. except SystemExit:
  186. raise
  187. except Exception:
  188. self.log.info("Unhandled exception in main loop",
  189. exc_info=True)
  190. self.stop(False)
  191. if self.pidfile is not None:
  192. self.pidfile.unlink()
  193. sys.exit(-1)
  194. def handle_chld(self, sig, frame):
  195. "SIGCHLD handling"
  196. self.reap_workers()
  197. self.wakeup()
  198. def handle_hup(self):
  199. """\
  200. HUP handling.
  201. - Reload configuration
  202. - Start the new worker processes with a new configuration
  203. - Gracefully shutdown the old worker processes
  204. """
  205. self.log.info("Hang up: %s", self.master_name)
  206. self.reload()
  207. def handle_term(self):
  208. "SIGTERM handling"
  209. raise StopIteration
  210. def handle_int(self):
  211. "SIGINT handling"
  212. self.stop(False)
  213. raise StopIteration
  214. def handle_quit(self):
  215. "SIGQUIT handling"
  216. self.stop(False)
  217. raise StopIteration
  218. def handle_ttin(self):
  219. """\
  220. SIGTTIN handling.
  221. Increases the number of workers by one.
  222. """
  223. self.num_workers += 1
  224. self.manage_workers()
  225. def handle_ttou(self):
  226. """\
  227. SIGTTOU handling.
  228. Decreases the number of workers by one.
  229. """
  230. if self.num_workers <= 1:
  231. return
  232. self.num_workers -= 1
  233. self.manage_workers()
  234. def handle_usr1(self):
  235. """\
  236. SIGUSR1 handling.
  237. Kill all workers by sending them a SIGUSR1
  238. """
  239. self.log.reopen_files()
  240. self.kill_workers(signal.SIGUSR1)
  241. def handle_usr2(self):
  242. """\
  243. SIGUSR2 handling.
  244. Creates a new master/worker set as a slave of the current
  245. master without affecting old workers. Use this to do live
  246. deployment with the ability to backout a change.
  247. """
  248. self.reexec()
  249. def handle_winch(self):
  250. """SIGWINCH handling"""
  251. if self.cfg.daemon:
  252. self.log.info("graceful stop of workers")
  253. self.num_workers = 0
  254. self.kill_workers(signal.SIGTERM)
  255. else:
  256. self.log.debug("SIGWINCH ignored. Not daemonized")
  257. def maybe_promote_master(self):
  258. if self.master_pid == 0:
  259. return
  260. if self.master_pid != os.getppid():
  261. self.log.info("Master has been promoted.")
  262. # reset master infos
  263. self.master_name = "Master"
  264. self.master_pid = 0
  265. self.proc_name = self.cfg.proc_name
  266. del os.environ['GUNICORN_PID']
  267. # rename the pidfile
  268. if self.pidfile is not None:
  269. self.pidfile.rename(self.cfg.pidfile)
  270. # reset proctitle
  271. util._setproctitle("master [%s]" % self.proc_name)
  272. def wakeup(self):
  273. """\
  274. Wake up the arbiter by writing to the PIPE
  275. """
  276. try:
  277. os.write(self.PIPE[1], b'.')
  278. except IOError as e:
  279. if e.errno not in [errno.EAGAIN, errno.EINTR]:
  280. raise
  281. def halt(self, reason=None, exit_status=0):
  282. """ halt arbiter """
  283. self.stop()
  284. self.log.info("Shutting down: %s", self.master_name)
  285. if reason is not None:
  286. self.log.info("Reason: %s", reason)
  287. if self.pidfile is not None:
  288. self.pidfile.unlink()
  289. self.cfg.on_exit(self)
  290. sys.exit(exit_status)
  291. def sleep(self):
  292. """\
  293. Sleep until PIPE is readable or we timeout.
  294. A readable PIPE means a signal occurred.
  295. """
  296. try:
  297. ready = select.select([self.PIPE[0]], [], [], 1.0)
  298. if not ready[0]:
  299. return
  300. while os.read(self.PIPE[0], 1):
  301. pass
  302. except select.error as e:
  303. if e.args[0] not in [errno.EAGAIN, errno.EINTR]:
  304. raise
  305. except OSError as e:
  306. if e.errno not in [errno.EAGAIN, errno.EINTR]:
  307. raise
  308. except KeyboardInterrupt:
  309. sys.exit()
  310. def stop(self, graceful=True):
  311. """\
  312. Stop workers
  313. :attr graceful: boolean, If True (the default) workers will be
  314. killed gracefully (ie. trying to wait for the current connection)
  315. """
  316. unlink = self.reexec_pid == self.master_pid == 0 and not self.systemd
  317. sock.close_sockets(self.LISTENERS, unlink)
  318. self.LISTENERS = []
  319. sig = signal.SIGTERM
  320. if not graceful:
  321. sig = signal.SIGQUIT
  322. limit = time.time() + self.cfg.graceful_timeout
  323. # instruct the workers to exit
  324. self.kill_workers(sig)
  325. # wait until the graceful timeout
  326. while self.WORKERS and time.time() < limit:
  327. time.sleep(0.1)
  328. self.kill_workers(signal.SIGKILL)
  329. def reexec(self):
  330. """\
  331. Relaunch the master and workers.
  332. """
  333. if self.reexec_pid != 0:
  334. self.log.warning("USR2 signal ignored. Child exists.")
  335. return
  336. if self.master_pid != 0:
  337. self.log.warning("USR2 signal ignored. Parent exists.")
  338. return
  339. master_pid = os.getpid()
  340. self.reexec_pid = os.fork()
  341. if self.reexec_pid != 0:
  342. return
  343. self.cfg.pre_exec(self)
  344. environ = self.cfg.env_orig.copy()
  345. environ['GUNICORN_PID'] = str(master_pid)
  346. if self.systemd:
  347. environ['LISTEN_PID'] = str(os.getpid())
  348. environ['LISTEN_FDS'] = str(len(self.LISTENERS))
  349. else:
  350. environ['GUNICORN_FD'] = ','.join(
  351. str(l.fileno()) for l in self.LISTENERS)
  352. os.chdir(self.START_CTX['cwd'])
  353. # exec the process using the original environment
  354. os.execvpe(self.START_CTX[0], self.START_CTX['args'], environ)
  355. def reload(self):
  356. old_address = self.cfg.address
  357. # reset old environment
  358. for k in self.cfg.env:
  359. if k in self.cfg.env_orig:
  360. # reset the key to the value it had before
  361. # we launched gunicorn
  362. os.environ[k] = self.cfg.env_orig[k]
  363. else:
  364. # delete the value set by gunicorn
  365. try:
  366. del os.environ[k]
  367. except KeyError:
  368. pass
  369. # reload conf
  370. self.app.reload()
  371. self.setup(self.app)
  372. # reopen log files
  373. self.log.reopen_files()
  374. # do we need to change listener ?
  375. if old_address != self.cfg.address:
  376. # close all listeners
  377. [l.close() for l in self.LISTENERS]
  378. # init new listeners
  379. self.LISTENERS = sock.create_sockets(self.cfg, self.log)
  380. listeners_str = ",".join([str(l) for l in self.LISTENERS])
  381. self.log.info("Listening at: %s", listeners_str)
  382. # do some actions on reload
  383. self.cfg.on_reload(self)
  384. # unlink pidfile
  385. if self.pidfile is not None:
  386. self.pidfile.unlink()
  387. # create new pidfile
  388. if self.cfg.pidfile is not None:
  389. self.pidfile = Pidfile(self.cfg.pidfile)
  390. self.pidfile.create(self.pid)
  391. # set new proc_name
  392. util._setproctitle("master [%s]" % self.proc_name)
  393. # spawn new workers
  394. for i in range(self.cfg.workers):
  395. self.spawn_worker()
  396. # manage workers
  397. self.manage_workers()
  398. def murder_workers(self):
  399. """\
  400. Kill unused/idle workers
  401. """
  402. if not self.timeout:
  403. return
  404. workers = list(self.WORKERS.items())
  405. for (pid, worker) in workers:
  406. try:
  407. if time.time() - worker.tmp.last_update() <= self.timeout:
  408. continue
  409. except (OSError, ValueError):
  410. continue
  411. if not worker.aborted:
  412. self.log.critical("WORKER TIMEOUT (pid:%s)", pid)
  413. worker.aborted = True
  414. self.kill_worker(pid, signal.SIGABRT)
  415. else:
  416. self.kill_worker(pid, signal.SIGKILL)
  417. def reap_workers(self):
  418. """\
  419. Reap workers to avoid zombie processes
  420. """
  421. try:
  422. while True:
  423. wpid, status = os.waitpid(-1, os.WNOHANG)
  424. if not wpid:
  425. break
  426. if self.reexec_pid == wpid:
  427. self.reexec_pid = 0
  428. else:
  429. # A worker was terminated. If the termination reason was
  430. # that it could not boot, we'll shut it down to avoid
  431. # infinite start/stop cycles.
  432. exitcode = status >> 8
  433. if exitcode == self.WORKER_BOOT_ERROR:
  434. reason = "Worker failed to boot."
  435. raise HaltServer(reason, self.WORKER_BOOT_ERROR)
  436. if exitcode == self.APP_LOAD_ERROR:
  437. reason = "App failed to load."
  438. raise HaltServer(reason, self.APP_LOAD_ERROR)
  439. worker = self.WORKERS.pop(wpid, None)
  440. if not worker:
  441. continue
  442. worker.tmp.close()
  443. self.cfg.child_exit(self, worker)
  444. except OSError as e:
  445. if e.errno != errno.ECHILD:
  446. raise
  447. def manage_workers(self):
  448. """\
  449. Maintain the number of workers by spawning or killing
  450. as required.
  451. """
  452. if len(self.WORKERS.keys()) < self.num_workers:
  453. self.spawn_workers()
  454. workers = self.WORKERS.items()
  455. workers = sorted(workers, key=lambda w: w[1].age)
  456. while len(workers) > self.num_workers:
  457. (pid, _) = workers.pop(0)
  458. self.kill_worker(pid, signal.SIGTERM)
  459. active_worker_count = len(workers)
  460. if self._last_logged_active_worker_count != active_worker_count:
  461. self._last_logged_active_worker_count = active_worker_count
  462. self.log.debug("{0} workers".format(active_worker_count),
  463. extra={"metric": "gunicorn.workers",
  464. "value": active_worker_count,
  465. "mtype": "gauge"})
  466. def spawn_worker(self):
  467. self.worker_age += 1
  468. worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS,
  469. self.app, self.timeout / 2.0,
  470. self.cfg, self.log)
  471. self.cfg.pre_fork(self, worker)
  472. pid = os.fork()
  473. if pid != 0:
  474. worker.pid = pid
  475. self.WORKERS[pid] = worker
  476. return pid
  477. # Process Child
  478. worker.pid = os.getpid()
  479. try:
  480. util._setproctitle("worker [%s]" % self.proc_name)
  481. self.log.info("Booting worker with pid: %s", worker.pid)
  482. self.cfg.post_fork(self, worker)
  483. worker.init_process()
  484. sys.exit(0)
  485. except SystemExit:
  486. raise
  487. except AppImportError as e:
  488. self.log.debug("Exception while loading the application",
  489. exc_info=True)
  490. print("%s" % e, file=sys.stderr)
  491. sys.stderr.flush()
  492. sys.exit(self.APP_LOAD_ERROR)
  493. except:
  494. self.log.exception("Exception in worker process"),
  495. if not worker.booted:
  496. sys.exit(self.WORKER_BOOT_ERROR)
  497. sys.exit(-1)
  498. finally:
  499. self.log.info("Worker exiting (pid: %s)", worker.pid)
  500. try:
  501. worker.tmp.close()
  502. self.cfg.worker_exit(self, worker)
  503. except:
  504. self.log.warning("Exception during worker exit:\n%s",
  505. traceback.format_exc())
  506. def spawn_workers(self):
  507. """\
  508. Spawn new workers as needed.
  509. This is where a worker process leaves the main loop
  510. of the master process.
  511. """
  512. for i in range(self.num_workers - len(self.WORKERS.keys())):
  513. self.spawn_worker()
  514. time.sleep(0.1 * random.random())
  515. def kill_workers(self, sig):
  516. """\
  517. Kill all workers with the signal `sig`
  518. :attr sig: `signal.SIG*` value
  519. """
  520. worker_pids = list(self.WORKERS.keys())
  521. for pid in worker_pids:
  522. self.kill_worker(pid, sig)
  523. def kill_worker(self, pid, sig):
  524. """\
  525. Kill a worker
  526. :attr pid: int, worker pid
  527. :attr sig: `signal.SIG*` value
  528. """
  529. try:
  530. os.kill(pid, sig)
  531. except OSError as e:
  532. if e.errno == errno.ESRCH:
  533. try:
  534. worker = self.WORKERS.pop(pid)
  535. worker.tmp.close()
  536. self.cfg.worker_exit(self, worker)
  537. return
  538. except (KeyError, OSError):
  539. return
  540. raise