arbiter.py 21 KB

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