gunicorn Arbiter 解析

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

  

Arbiter主要有以下方法:

setup:

处理配置项,最重要的是worker数量和worker工作模型

init_signal

注册信号处理函数

handle_xxx:

各个信号具体的处理函数

kill_worker,kill_workers:

向worker进程发信号

spawn_worker, spawn_workers:

fork出新的worker进程

murder_workers:

杀掉一段时间内未响应的worker进程

manage_workers:

根据配置文件的worker数量,以及当前active的worker数量,决定是要fork还是kill worker进程

reexec

接收到信号SIGUSR2调用,在线升级gunicorn

reload:

接收到信号SIGHUP调用,会根据新的配置新启动worker进程,并杀掉之前的worker进程

sleep

在没有信号处理的时候,利用select的timeout进行sleep,可被唤醒

wakeup

通过向管道写消息,唤醒进程

run

主循环

  Arbiter真正被其他代码(Application)调用的函数只有__init__和run方法,在一句代码里:

Arbiter(self).run()

  上面代码中的self即为Application实例,其中__init__调用setup进行配置项设置。下面是run方法伪代码

def run()
    self.init_signal()
    self.LISTENERS = create_sockets(self.cfg, self.log)
    self.manage_workers()
    while True:
        if no signal in SIG_QUEUE
            self.sleep()
        else:
            handle_signal()

 关于fork子进程

  fork子进程的代码在 spawn_worker, 源码如下:

  

 1     def spawn_worker(self):
 2         self.worker_age += 1
 3         worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS,
 4                                    self.app, self.timeout / 2.0,
 5                                    self.cfg, self.log)
 6         self.cfg.pre_fork(self, worker)
 7         pid = os.fork()
 8         if pid != 0:
 9             self.WORKERS[pid] = worker
10             return pid
11
12         # Process Child
13         worker_pid = os.getpid()
14         try:
15             util._setproctitle("worker [%s]" % self.proc_name)
16             self.log.info("Booting worker with pid: %s", worker_pid)
17             self.cfg.post_fork(self, worker)
18             worker.init_process()
19             sys.exit(0)
20         except SystemExit:
21             raise
22         except AppImportError as e:
23             self.log.debug("Exception while loading the application",
24                            exc_info=True)
25             print("%s" % e, file=sys.stderr)
26             sys.stderr.flush()
27             sys.exit(self.APP_LOAD_ERROR)
28         except:
29             self.log.exception("Exception in worker process"),
30             if not worker.booted:
31                 sys.exit(self.WORKER_BOOT_ERROR)
32             sys.exit(-1)
33         finally:
34             self.log.info("Worker exiting (pid: %s)", worker_pid)
35             try:
36                 worker.tmp.close()
37                 self.cfg.worker_exit(self, worker)
38             except:
39                 self.log.warning("Exception during worker exit:\n%s",
40                                   traceback.format_exc())

Arbiter.spawn_worker

  主要流程:

(1)加载worker_class并实例化(默认为同步模型 SyncWorker)

(2)父进程(master进程)fork之后return,之后的逻辑都在子进程中运行

(3)调用worker.init_process 进入循环,worker的所有工作都在这个循环中

(4)循环结束之后,调用sys.exit(0)

(5)最后,在finally中,记录worker进程的退出

下面是我自己写的一点代码,把主要的fork流程简化了一下

 1 # prefork.py
 2 import sys
 3 import socket
 4 import select
 5 import os
 6 import time
 7
 8 def do_sub_process():
 9     pid = os.fork()
10     if pid < 0:
11         print ‘fork error‘
12         sys.exit(-1)
13     elif pid > 0:
14         print ‘fork sub process %d‘  % pid
15         return
16
17     # must be child process
18     time.sleep(1)
19     print ‘sub process will exit‘, os.getpid(), os.getppid()
20     sys.exit(0)
21
22 def main():
23     sub_num = 2
24     for i in range(sub_num):
25         do_sub_process()
26     time.sleep(10)
27     print ‘main process will exit‘, os.getpid()
28
29 if __name__ == ‘__main__‘:
30     main()

在测试环境下输出:

  fork sub process 9601

  fork sub process 9602

  sub process will exit 9601 9600

  sub process will exit 9602 9600

  main process will exit 9600

  需要注意的是第20行调用了sys.exit, 保证子进程的结束,否则会继续main函数中for循环,以及之后的逻辑。注释掉第19行重新运行,看输出就明白了。

关于kill子进程

  master进程要kill worker进程就很简单了,直接发信号,源码如下:

  

 1     def kill_worker(self, pid, sig):
 2         """ 3         Kill a worker
 4
 5         :attr pid: int, worker pid
 6         :attr sig: `signal.SIG*` value
 7          """
 8         try:
 9             os.kill(pid, sig)
10         except OSError as e:
11             if e.errno == errno.ESRCH:
12                 try:
13                     worker = self.WORKERS.pop(pid)
14                     worker.tmp.close()
15                     self.cfg.worker_exit(self, worker)
16                     return
17                 except (KeyError, OSError):
18                     return
19             raise

关于sleep与wakeup

  我们再来看看Arbiter的sleep和wakeup。Arbiter在没有信号需要处理的时候会"sleep",当然,不是真正调用time.sleep,否则信号来了也不能第一时间处理。这里得实现比较巧妙,利用了管道和select的timeout。看代码就知道了

        def sleep(self):
        """        Sleep until PIPE is readable or we timeout.
        A readable PIPE means a signal occurred.
        """
            ready = select.select([self.PIPE[0]], [], [], 1.0) # self.PIPE = os.pipe()
            if not ready[0]:
                return
            while os.read(self.PIPE[0], 1):
                pass

  代码里面的注释写得非常清楚,要么PIPE可读立即返回,要么等待超时。管道可读是因为有信号发生。这里看看pipe函数

  os.pipe()

Create a pipe. Return a pair of file descriptors (r,w) usable for reading and writing, respectively.

  那我们看一下什么时候管道可读:肯定是往管道写入的东西,这就是wakeup函数的功能

        def wakeup(self):
            """
            Wake up the arbiter by writing to the PIPE
            """
            os.write(self.PIPE[1], b‘.‘)

最后附上Arbiter的信号处理

  • QUITINT: Quick shutdown
  • TERM: Graceful shutdown. Waits for workers to finish their current requests up to the graceful timeout.
  • HUP: Reload the configuration, start the new worker processes with a new configuration and gracefully shutdown older workers. If the application is not preloaded (using the --preloadoption), Gunicorn will also load the new version.
  • TTIN: Increment the number of processes by one
  • TTOU: Decrement the number of processes by one
  • USR1: Reopen the log files
  • USR2: Upgrade the Gunicorn on the fly. A separate TERM signal should be used to kill the old process. This signal can also be used to use the new versions of pre-loaded applications.
  • WINCH: Gracefully shutdown the worker processes when Gunicorn is daemonized.

reference:

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

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

时间: 2024-12-21 03:47:50

gunicorn Arbiter 解析的相关文章

gunicorn Arbiter 源码解析

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

gunicorn 简介

  gunicorn是一个python Wsgi http server,只支持在Unix系统上运行,来源于Ruby的unicorn项目.Gunicorn使用prefork master-worker模型(在gunicorn中,master被称为arbiter),能够与各种wsgi web框架协作.gunicorn的文档是比较完善的,这里也有部分中文翻译,不过还是建议直接读英文文档.笔者并没有在项目中真正使用过gunicorn,阅读其源码只要是为了了解其master worker模型,如有纰漏,

zato server启动后自动关闭问题解决

症状 zato start server 启动server后,过一会server自动关闭了 解决 查看日志 UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 269: ordinal not in range(128) Traceback (most recent call last): File "/opt/zato/2.0.5/eggs/gunicorn-18.0-py2.7.egg/gunicorn/a

gunicorn syncworker 源码解析

gunicorn支持不同的worker类型,同步或者异步,异步的话包括基于gevent.基于eventlet.基于Aiohttp(python版本需要大于3.3),也有多线程的版本.下面是gunicorn当前版本(19.6.0)支持的Worker类型: sync eventlet - Requires eventlet >= 0.9.7 gevent - Requires gevent >= 0.13 tornado - Requires tornado >= 0.2 gthread -

Nginx+Gunicorn+virtualenv+supervisord+Postgresql部署Django应用

关于Django应用部署 Django是一个高效.多功能和动态地进化的Web应用开发框架.目前比较流行的部署.运行Django应用方式是基于Apache的mod_wsgi模块,但更加高效.弹性,同时又更加复杂的方式是使用以下工具来部署实施:Nginx.Gunicorn.virtualenv.supervisord.Postgresql.以下详细介绍如何结合这些工具来部署Django应用到Linux上. 准备工作 需要有一台拥有root权限的Linux服务器,这是部署应用的平台.本文采用CentO

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. SIGU

gunicorn syncworker

gunicorn支持不同的worker类型,同步或者异步,异步的话包括基于gevent.基于eventlet.基于Aiohttp(python版本需要大于3.3),也有多线程的版本.下面是gunicorn当前版本(19.6.0)支持的Worker类型: sync eventlet - Requires eventlet >= 0.9.7 gevent - Requires gevent >= 0.13 tornado - Requires tornado >= 0.2 gthread -

HTTP——HTTP 1.1的详细介绍 Gunicorn不支持HTTP 1.1

从前面一小节的表格里,我们可以看到,Gunicorn 的一个缺点是不支持HTTP 1.1.那么 HTTP 1.1 究竟是怎么一回事呢?我们选择 HTTP 服务器在什么情况下需要考虑对 HTTP 1.1 的支持? HTTP 1.1与HTTP 1.0的比较 一个WEB站点每天可能要接收到上百万的用户请求,为了提高系统的效率,HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请

WSGI及gunicorn指北(二)

pyg0已经大概了解了wsgi.现在他决定深入探索他们实际在生产环境里用到的web 服务器 -gunicorn. 先来看看官网的介绍:Gunicorn 是一个运行在Unix上的python WSGI web 服务器.它采用Pre-Fork 的worker模型 .Gunicorn可以适用于多种python web 框架,实现简单,占用系用资源少,速度快. 抛开那些自卖自夸的话语,我们一项一项来分析. Gunicorn 之所以只能运行在Unix系统上,是因为gunicorn使用了只有在*nix系统中