gunicorn 信号处理(SIGHUP,SIGUSR2)

 

这篇文章中,提到了Master进程对信号的处理函数,其中有两个信号比较有意思。

SIGHUP:用来热更新(Reload)应用

SIGUSR2:用来在线升级(upgrade on the fly)gunicorn

下面来详细看一下:

SIGHUP:

Reload the configuration, start the new worker processes with a new configuration and gracefully shutdown older workers.

SIGUP对应的信号处理函数是Arbiter.reload。简化后的核心代码如下:

 1     def reload(self):
 2         old_address = self.cfg.address
 3
 4         # reload conf,重新加载配置
 5         self.app.reload()
 6         self.setup(self.app)
 7
 8         # reopen log files
 9         self.log.reopen_files()
10
11         # do we need to change listener ?,处理监听端口变化的情况
12         if old_address != self.cfg.address:
13             # close all listeners
14             [l.close() for l in self.LISTENERS]
15             # init new listeners
16             self.LISTENERS = create_sockets(self.cfg, self.log)
17             listeners_str = ",".join([str(l) for l in self.LISTENERS])
18             self.log.info("Listening at: %s", listeners_str)
19
20         # spawn new workers,启动新的Worker(数量和类型来自新的配置)
21         for i in range(self.cfg.workers):
22             self.spawn_worker()
23
24         # manage workers,这里会kill掉原来的Worker
25         self.manage_workers()

  在上面的引用中提到,会“优雅”(graceful)地kill掉老的worker进程。实现也很简单,Arbiter为每一个fork出的worker进程设置一个自增的“worker_age”,worker中这个属性越小,表明这个worker越老。在manage_workers中,如果已经启动的worker进程数量大于配置中的数量,那么会kill掉比较老的worker进程。代码如下

 1     def manage_workers(self):
 2         """ 3         Maintain the number of workers by spawning or killing
 4         as required.
 5         """
 6         if len(self.WORKERS.keys()) < self.num_workers:
 7             self.spawn_workers()
 8
 9         workers = self.WORKERS.items()
10         workers = sorted(workers, key=lambda w: w[1].age) # 按worker的age顺序排序
11         while len(workers) > self.num_workers: # num_workers是配置的worker数量
12             (pid, _) = workers.pop(0)
13             self.kill_worker(pid, signal.SIGTERM)

SIGUSR2

Upgrade the Gunicorn on the fly. A separate TERM signal should be used to kill the old process.

  对应的信号处理函数是Arbiter.reexec,该函数会重新fork出新的master-workers进程,但不会影响到原来的master,worker进程,所以上面提到需要将原来的进程kill掉。

 1     def reexec(self):
 2         """ 3         Relaunch the master and workers.
 4         """
 5         if self.reexec_pid != 0:
 6             self.log.warning("USR2 signal ignored. Child exists.")
 7             return
 8
 9         if self.master_pid != 0:
10             self.log.warning("USR2 signal ignored. Parent exists")
11             return
12
13         master_pid = os.getpid()
14         self.reexec_pid = os.fork()
15         if self.reexec_pid != 0:
16             return
17
18         self.cfg.pre_exec(self)
19
20         environ = self.cfg.env_orig.copy()
21         fds = [l.fileno() for l in self.LISTENERS]
22         environ[‘GUNICORN_FD‘] = ",".join([str(fd) for fd in fds]) # 设置了一些环境变量,用来区分是正常启动gunicorn还是通过fork重新启动。
23         environ[‘GUNICORN_PID‘] = str(master_pid)
24
25         os.chdir(self.START_CTX[‘cwd‘])
26
27         # exec the process using the original environnement
28         os.execvpe(self.START_CTX[0], self.START_CTX[‘args‘], environ)

下面做一下实验,两个终端,1号终端运行代码,2号终端发送信号。

step1

1号终端启动gunirorn: gunicorn -w 2 gunicorn_app:app ,

2号终端查看python进程: ps -ef | grep python,

1号终端输出

[2017-01-19 15:21:23 +0000] [6166] [INFO] Starting gunicorn 19.6.0

[2017-01-19 15:21:23 +0000] [6166] [INFO] Listening at: http://127.0.0.1:8000 (6166)

[2017-01-19 15:21:23 +0000] [6166] [INFO] Using worker: sync

[2017-01-19 15:21:23 +0000] [6171] [INFO] Booting worker with pid: 6171

[2017-01-19 15:21:23 +0000] [6172] [INFO] Booting worker with pid: 6172

2号终端输出

hzliumi+  6166  5721  0 15:21 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6171  6166  0 15:21 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6172  6166  0 15:21 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

可以看到,master进程的PID是6166,然后两个worker进程的pid分别是6171、6172. 下面需要用到master进程的PID

step2

2号终端发送信号:kill -SIGUSR2 6166 , 然后查看python进程

1号终端追加输出

[2017-01-19 15:27:08 +0000] [6166] [INFO] Handling signal: usr2

[2017-01-19 15:27:08 +0000] [6629] [INFO] Starting gunicorn 19.6.0

[2017-01-19 15:27:08 +0000] [6629] [INFO] Listening at: http://127.0.0.1:8000 (6629)

[2017-01-19 15:27:08 +0000] [6629] [INFO] Using worker: sync

[2017-01-19 15:27:08 +0000] [6634] [INFO] Booting worker with pid: 6634

[2017-01-19 15:27:08 +0000] [6635] [INFO] Booting worker with pid: 6635

2号终端输出

hzliumi+  6166  5721  0 15:21 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6171  6166  0 15:21 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6172  6166  0 15:21 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6629  6166  3 15:27 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6634  6629  0 15:27 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6635  6629  0 15:27 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

从1号终端可以看出,基本是重新启动了gunicorn,不过从2号终端输出可以看到,新的master进程(pid 6629)是老的master进程(pid 6166)的子进程

 

step3

2号终端发送信号:kill -SIGTERM 6166 , 稍等几秒后查看python进程:

1号终端追加输出

[2017-01-19 15:29:42 +0000] [6166] [INFO] Handling signal: term

[2017-01-19 15:29:42 +0000] [6171] [INFO] Worker exiting (pid: 6171)

[2017-01-19 15:29:42 +0000] [6172] [INFO] Worker exiting (pid: 6172)

[2017-01-19 15:29:42 +0000] [6166] [INFO] Shutting down: Master

[2017-01-19 15:29:42 +0000] [6629] [INFO] Master has been promoted.

2号终端输出

hzliumi+  6629     1  0 15:27 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6634  6629  0 15:27 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

hzliumi+  6635  6629  0 15:27 pts/0    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w 2 gunicorn_app:app

可以看到,老的master进程(6166)及其fork出的worker子进程(pid分别是6171、6172)已经被kill掉了

references:

http://docs.gunicorn.org/en/stable/signals.html

时间: 2025-01-22 20:51:44

gunicorn 信号处理(SIGHUP,SIGUSR2)的相关文章

转-Unix系统进程对SIGTERM、SIGUSR1和SIGUSR2信号处理

Unix系统进程对SIGTERM.SIGUSR1和SIGUSR2信号处理 作者:vfhky | 时间:2015-08-05 17:41 | 分类:cseries 好久没更新博客了,写篇文章除除草.这篇文章主要通过简单的例子说明一下Unix/Linux进程中如果捕捉和处理SIGTERM.SIGUSR1和SIGUSR2信号. 先说明一下这三个信号: 信号(signal)是*nix系统中进程之间通信(IPC)的一种常见方式. SIGTERM:进程终止信号,效果等同于*nix shell中不带-9的ki

gunicorn Arbiter 解析

如前文所述,Arbiter是gunicorn master进程的核心.Arbiter主要负责管理worker进程,包括启动.监控.杀掉Worker进程:同时,Arbiter在某些信号发生的时候还可以热更新(reload)App应用,或者在线升级gunicorn.Arbiter的核心代码在一个文件里面,代码量也不大,源码在此:https://github.com/benoitc/gunicorn. Arbiter主要有以下方法: setup: 处理配置项,最重要的是worker数量和worker工

gunicorn Arbiter 源码解析

如前文所述,Arbiter是gunicorn master进程的核心.Arbiter主要负责管理worker进程,包括启动.监控.杀掉Worker进程:同时,Arbiter在某些信号发生的时候还可以热更新(reload)App应用,或者在线升级gunicorn.Arbiter的核心代码在一个文件里面,代码量也不大,源码在此:https://github.com/benoitc/gunicorn. Arbiter主要有以下方法: setup: 处理配置项,最重要的是worker数量和worker工

Linux信号、信号处理和信号处理函数

信号(signal)是一种软件中断,它提供了一种处理异步事件的方法,也是进程间惟一的异步通信方式.在Linux系统中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某种程序发生了什么事件,还可以给进程传递数据. 一.信号的来源 信号的来源可以有很多种试,按照产生条件的不同可以分为硬件和软件两种. 1.  硬件方式 当用户在终端上按下某键时,将产生信号.如按下组合键后将产生一个SIGINT信号. 硬件异常产生信号:除数据.无效的存储访问等.这些事件通常由硬件(如:CPU)检测到,并将其通知

Android信号处理

首先澄清,本文讨论的信号是 Linux 软中断信号,而不是手机状态条里面用于显示当前手机通信强度的那个信号. 我们知道,Unix系统里信号是一种软中断.尽管本身存在缺陷(后面会讨论到),但是作为Unix系统重要的异步事件处理方式之一,在Unix系统中发挥重要的作用.可以说,所有Unix系统(包括Linux)都不可能忽略信号的支持. Android 本质上也是个在 Linux 系统,自然也少不了对 信号处理的支持. 但我们也知道,Android和其他Linux系统一个很大的差异就是增加了虚拟机的支

内核信号处理 &amp; CPU8个通用寄存器

内核信号处理参考: http://www.spongeliu.com/165.html 信号本质上是在软件层次上对中断机制的一种模拟(注意区分中断.异常.信号),其主要有以下几种来源: 程序错误:除零,非法内存访问- 外部信号:终端Ctrl-C产生SGINT信号,定时器到期产生SIGALRM- 显式请求:kill函数允许进程发送任何信号给其他进程或进程组. 在Linux下,可以通过以下命令查看系统所有的信号: kill -l 可以通过类似下面的命令显式的给一个进程发送一个信号: kill -2

Linux程序设计学习笔记——异步信号处理机制

转载请注明出处: http://blog.csdn.net/suool/article/details/38453333 Linux常见信号与处理 基本概念 Linux的信号是一种进程间异步的通信机制,在实现上一种软中断.信号能够导致一个正在执行的进程被异步打断,转而去处理一个突发事件.异步事件不可预知,仅仅能通过一些特定方式预防.或者说,当该异步事件发生时依据原来的设定完毕对应的操作. 信号本质 信号是在软件层次上对中断机制的一种模拟.在原理上,一个进程收到一个信号与处理器收到一个中断请求能够

推荐一个多线程信号处理的文章

The odd thing about signals in UNIX is that, although they're everywhere, their arrival≍by its very nature≍is always a bit of surprise. (Well, that's a bit of an exaggeration. When we're told that the furniture delivery person will be at our house be

5)Linux程序设计入门--信号处理

5)Linux程序设计入门--信号处理 Linux下的信号事件 前言:这一章我们讨论一下Linux下的信号处理函数. Linux下的信号处理函数: 信号的产生 信号的处理 其它信号函数 一个实例 1.信号的产生 Linux下的信号可以类比于DOS下的INT或者是Windows下的事件.在有一个信号发生时 候相信的信号就会发送给相应的进程.在Linux下的信号有以下几个. 我们使用 kill -l 命令可以得到以下的输出结果: 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) S