这篇文章中,提到了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。简化后的核心代码如下:
     def reload(self):
old_address = self.cfg.address # reload conf,重新加载配置
self.app.reload()
self.setup(self.app) # reopen log files
self.log.reopen_files() # do we need to change listener ?,处理监听端口变化的情况
if old_address != self.cfg.address:
# close all listeners
[l.close() for l in self.LISTENERS]
# init new listeners
self.LISTENERS = create_sockets(self.cfg, self.log)
listeners_str = ",".join([str(l) for l in self.LISTENERS])
self.log.info("Listening at: %s", listeners_str) # spawn new workers,启动新的Worker(数量和类型来自新的配置)
for i in range(self.cfg.workers):
self.spawn_worker() # manage workers,这里会kill掉原来的Worker
self.manage_workers()
  在上面的引用中提到,会“优雅”(graceful)地kill掉老的worker进程。实现也很简单,Arbiter为每一个fork出的worker进程设置一个自增的“worker_age”,worker中这个属性越小,表明这个worker越老。在manage_workers中,如果已经启动的worker进程数量大于配置中的数量,那么会kill掉比较老的worker进程。代码如下
     def manage_workers(self):
"""\
Maintain the number of workers by spawning or killing
as required.
"""
if len(self.WORKERS.keys()) < self.num_workers:
self.spawn_workers() workers = self.WORKERS.items()
workers = sorted(workers, key=lambda w: w[1].age) # 按worker的age顺序排序
while len(workers) > self.num_workers: # num_workers是配置的worker数量
(pid, _) = workers.pop(0)
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掉。
     def reexec(self):
"""\
Relaunch the master and workers.
"""
if self.reexec_pid != 0:
self.log.warning("USR2 signal ignored. Child exists.")
return if self.master_pid != 0:
self.log.warning("USR2 signal ignored. Parent exists")
return master_pid = os.getpid()
self.reexec_pid = os.fork()
if self.reexec_pid != 0:
return self.cfg.pre_exec(self) environ = self.cfg.env_orig.copy()
fds = [l.fileno() for l in self.LISTENERS]
environ['GUNICORN_FD'] = ",".join([str(fd) for fd in fds]) # 设置了一些环境变量,用来区分是正常启动gunicorn还是通过fork重新启动。
environ['GUNICORN_PID'] = str(master_pid) os.chdir(self.START_CTX['cwd']) # exec the process using the original environnement
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:

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

  1. [10]APUE:信号

    [a] 常用信号 SIGABRT 调用 abort 函数时产生此信号,进程异常终止 SIGALRM 调用 alarm 或 setitimer 函数超时之后产生 SIGCHLD 子进程终止或 stop ...

  2. nginx的平滑升级

    一:解释nginx的平滑升级 随着nginx越来越流行,并且nginx的优势也越来越明显,nginx的版本迭代也来时加速模式,1.9.0版本的nginx更新了许多新功能,例如stream四层代理功能, ...

  3. gunicorn Arbiter 源码解析

    如前文所述,Arbiter是gunicorn master进程的核心.Arbiter主要负责管理worker进程,包括启动.监控.杀掉Worker进程:同时,Arbiter在某些信号发生的时候还可以热 ...

  4. Linux 多线程应用中如何编写安全的信号处理函数

    http://blog.163.com/he_junwei/blog/static/1979376462014021105242552/ http://www.ibm.com/developerwor ...

  5. linux 下信号处理命令trap && linux下各种信号的意义

    1.用途说明 trap是一个shell内建命令,它用来在脚本中指定信号如何处理.比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出 ...

  6. Perl信号处理

    本文关于Perl信号处理的内容主体来自于<Pro Perl>的第21章. 信号处理 操作系统可以通过信号(signal)处理机制来实现一些功能:程序注册好待监视的信号处理机制,在程序运行过 ...

  7. Android 信号处理面面观 之 信号定义、行为和来源

    总结: Android中: Sending signal. PID: XXX SIG: 3   ====>打印trace 原文:http://blog.csdn.net/rambo2188/ar ...

  8. linux 信号处理 五 (示例)

    [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执 ...

  9. linux 信号处理 一 (基本概念)

    信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念.Linux对信号机制的大致实现方法.如何使用信号,以及有关信号的几个系统调用. 信号机制是进程之间相互传递消息的一种方法,信号全 ...

随机推荐

  1. Java多线程学习之线程池源码详解

    0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...

  2. MySQL长短密码

    MySQL长短密码 今天批量搭建MySQL环境的时候,遇到长短密码问题,故就此问题总结一下长短密码. 介绍 1.长密码例子: mysql> show grants for 'test'@'loc ...

  3. 使用JavaScript生成二维码教程-附qrcodejs中文文档

    使用javascript生成二维码 依赖jquery 需要使用到的库 https://github.com/davidshimjs/qrcodejs DIV <div id="qrco ...

  4. Postman+newman+jenkins构建

      最近忙着项目接口测试,经过不同工具的对比,发现postman使用起来挺顺手的,所以马上决定使用这个工具进行接口测试工作.刚开始的时候,了解了下接口测试的相关信息,直接着手编写接口测试的测试用例信息 ...

  5. TIJ学习总结(1)- Java基础语法

    TIJ(Thinking in Java)作为Java学习书籍里的"圣经",之前花两个月系统的捋了一遍,很多东西有种豁然开朗的感觉,入门之后读一遍TIJ,相信会有很多意外收获哦- ...

  6. NodeMCU Builder, yet another NodeMCU IDE

    最近几天研究基于NodeMCU的Wi-Fi小车,突然之间想要写一个专门开发NodeMCU Lua代码的工具自己用,由于官方已经有了NodeMCU Studio,所以我的就叫NodeMCU Builde ...

  7. P1457 城堡 The Castle

    轻度中毒 原题 :The Castle 以下为题解部分:明明辣么简单的一道题,硬是搞了1.5h,WTF?以下列出本题的一些要点. 搜索(DFS)嘛,染色嘛,统计大小嘛,很容易想,也很更易处理. 接下来 ...

  8. tpshop使用自带极光推送

    推送逻辑在common里面的PushLogic.php;在后台网站->商城设置->推送设置,将极光的ID,密码填一下系统会自动加入数据库里面. 在你所需推送的PHP类里面引用这个文件即可. ...

  9. 大神博客链接系列---C#SubSonic3.0搭建ORM

    一.C#框架 C#ORM框架: SubSonic3.0制作ORM--- http://www.cnblogs.com/EmptyFS/p/3659679.html

  10. thinkphp系列:类的自动加载是如何设计的

    在使用框架开发时,可以发现框架有很多核心类,却很少看到显示的引入某个文件的代码,这是因为框架都采用了类的自动加载机制,即使用到类时,框架会自动找到该类所在文件的位置并引入该文件.为了更容易看出代码思路 ...