@orangleliu
2016-07-13T15:13:27.000000Z
字数 3458
阅读 2701
源码阅读
gunicorn
源码分析的一个项目,之前基本都是浏览各种源码,对其中的细节看的少,没有总结,也没有更高层次的归纳,所以这次认真的来分析一个Py项目的源码。 gunicorn是python的一个http服务器,来看看怎样设计和实现一个HTTP服务器。
没有任何目的的研究可能也收获不了什么。
阅读时候在源码上加了一些注释,放到github gunicorn-souce-reading
参考资料:
我这里是 git clone
出来源码之后,直接使用软连接替换 virtualenv
中的gunicorn源码来实现调试的。
这里的版本是19.45。 源码包中包含了 docs(文档),examples(案例),tests(测试)以及 gunicorn(py源码)还有一些安装脚本,说明信息等。
py源码的目录结构。
gunicorn
├── __init__.py
├── _compat.py
├── app
│ ├── __init__.py
│ ├── base.py
│ ├── django_wsgi.py
│ ├── djangoapp.py
│ ├── pasterapp.py
│ └── wsgiapp.py
├── arbiter.py
├── argparse_compat.py
├── config.py
├── debug.py
├── errors.py
├── glogging.py
├── http
│ ├── __init__.py
│ ├── _sendfile.py
│ ├── body.py
│ ├── errors.py
│ ├── message.py
│ ├── parser.py
│ ├── unreader.py
│ └── wsgi.py
├── instrument
│ ├── __init__.py
│ └── statsd.py
├── management
│ ├── __init__.py
│ └── commands
│ ├── __init__.py
│ └── run_gunicorn.py
├── pidfile.py
├── reloader.py
├── selectors.py
├── six.py
├── sock.py
├── util.py
└── workers
├── __init__.py
├── _gaiohttp.py
├── async.py
├── base.py
├── gaiohttp.py
├── geventlet.py
├── ggevent.py
├── gthread.py
├── gtornado.py
├── sync.py
└── workertmp.py
这模式从早期的版本就有了,github上最早能看到的tag是0.2, 那个版本比较容易理解,worker是同步模式,功能相对少一些。7.0版本开始添加 async的支持,worker可以有多种选择。
核心是arbiter.py文件,通过信号来控制进程的行为,需要一些操作系统和系统编程的知识才比较好理解。
涉及到的信号
通读了这段代码之后,pipe 管道和 signal 信号相互怎么作用的一直没明白。 为什么要用 pipe的读写来唤醒和休眠进程? 信号发出之后怎样添加到signal queue中的?
想法: 然后我就把所有的wakeup和sleep的代码都注释掉,发现程序运行的好好的,就是说整个master的loop频率非常快。 这个时候把 sleep开启(wakeup仍然注释),调整的5秒,再次测试的时候发现loop变成5s了,使用Ctrl+c
来停止进程,有时候甚至是无法响应的,有时候可以接受到信号。 再接着把wakeup打开,sleep调整到5s, Ctrl+c
就能即时响应了,loop时间还是5s左右。 所以猜测这里用pipe读写+select 来减少master loop的资源消耗,并仍能即使响应系统信号。
__ini__()
初始化各种参数,包括设置app,app的配置run()
开始进程start()
写入pidinit_signals()
初始化所有信号的handler,重新创建pipecreate_sockets
创建监听的socketworker_class
是否要配置不同的workerworker进程才是主要干活的工人,之前就说gunicorn提供了多个类型的worker,来看看他们是怎么实现的?代码在workers目录下。 worker的主要功能:
1. 对信号的处理
2. 接受socket内容,解析成http,遵循wsgi调用返回http响应
这里是其他worker的父类,定义了接口和公共方法(进程处理相关的居多) ,各种worker在会具体的实现这些接口。
默认的是一种同步的worker,也是最简单和最早的类型。
大概工作的流程:
util.seed()
util.close_on_exec()
防止文件描述符继承self.init_signals()
post_worker_init
加载 server hookself.load_wsgi()
run_for_one
)或者多个socket的监听 (run_for_multiple
)self.accept()
, 使用的是select 来处理事件循环。怎样从conn和handle 变成 reqeust和response的呢?
如果想使用基于gevent的worker使用 -k
参数 gunicorn test:app -k gevent
继承关系 base.Worker -> AsyncWorker -> GeventWorker
多线程的worker,依赖futures模块,使用线程来处理每个连接,用到了线程池。