@orangleliu
2016-07-13T07:13:27.000000Z
字数 3458
阅读 2982
源码阅读 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模块,使用线程来处理每个连接,用到了线程池。