0

0

如何进行gunicorn Arbiter 源码解析

PHPz

PHPz

发布时间:2023-05-12 16:28:18

|

1526人浏览过

|

来源于亿速云

转载

如前文所述,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, 源码如下:

如何进行gunicorn Arbiter 源码解析 Arbiter.spawn_worker

  主要流程:

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

JTBC网站内容管理系统5.0.3.1
JTBC网站内容管理系统5.0.3.1

JTBC CMS(5.0) 是一款基于PHP和MySQL的内容管理系统原生全栈开发框架,开源协议为AGPLv3,没有任何附加条款。系统可以通过命令行一键安装,源码方面不基于任何第三方框架,不使用任何脚手架,仅依赖一些常见的第三方类库如图表组件等,您只需要了解最基本的前端知识就能很敏捷的进行二次开发,同时我们对于常见的前端功能做了Web Component方式的封装,即便是您仅了解HTML/CSS也

下载

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

    (3)调用worker.init_process 进入循环,新航道雅思培训的所有工作都在这个循环中

    (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'  % pid15         return16  17     # must be child process18     time.sleep(1)19     print 'sub process will exit', os.getpid(), os.getppid()20     sys.exit(0)21  22 def main():23     sub_num = 224     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                     return17                 except (KeyError, OSError):18                     return19             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的信号处理

退出,INT:快速关闭

TERM: 优雅关机。等待工作人员完成其当前请求,直到超时。

HUP:重新加载配置,用新配置启动新的工作进程,并优雅地关闭旧的工作进程。如果应用程序未预加载(使用--preload选项),Gunicorn也将加载新版本。

TTIN:将进程数增加一个

TTOU:将进程数减少一个

USR1:重新打开日志文件

USR2:在飞行中升级Gunicorn。应使用单独的术语信号终止旧进程。此信号也可用于使用预加载应用程序的新版本。

绞盘:当Gunicorn被守护时,优雅地关闭工作进程。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

538

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

52

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

197

2025.08.29

http与https有哪些区别
http与https有哪些区别

http与https的区别:1、协议安全性;2、连接方式;3、证书管理;4、连接状态;5、端口号;6、资源消耗;7、兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1976

2024.08.16

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

9

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

21

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

13

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

33

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号