简介
Gunicorn“绿色独角兽”是一个被广泛使用的高性能的Python WSGI UNIX HTTP服务器,移植自Ruby的独角兽(Unicorn )项目,使用pre-fork worker模式,具有使用非常简单,轻量级的资源消耗,以及高性能等特点。
Gunicorn 服务器作为wsgi app的容器,能够与各种Web框架兼容(flask,django等),得益于gevent等技术,使用Gunicorn能够在基本不改变wsgi app代码的前提下,大幅度提高wsgi app的性能。
架构
服务模型(Server Model)
Gunicorn是基于 pre-fork 模型的。也就意味着有一个中心管理进程( master process )用来管理 worker 进程集合。Master从不知道任何关于客户端的信息。所有的请求和响应处理都是由 worker 进程来处理的。
Master(管理者)
主程序是一个简单的循环,监听各种信号以及相应的响应进程。master管理着正在运行的worker集合,通过监听各种信号比如TTIN, TTOU, and CHLD. TTIN and TTOU响应的增加和减少worker的数目。CHLD信号表明一个子进程已经结束了,在这种情况下master会自动的重启失败的worker。
worker
woker有很多种,包括:ggevent、geventlet、gtornado等等。这里主要分析ggevent。
每个ggevent worker启动的时候会启动多个server对象:worker首先为每个listener创建一个server对象(注:为什么是一组listener,因为gunicorn可以绑定一组地址,每个地址对于一个listener),每个server对象都有运行在一个单独的gevent pool对象中。真正等待链接和处理链接的操作是在server对象中进行的。
#为每个listener创建server对象。
for s in self.sockets:
pool = Pool(self.worker_connections) #创建gevent pool
if self.server_class is not None:
#创建server对象
server = self.server_class(
s, application=self.wsgi, spawn=pool, log=self.log,
handler_class=self.wsgi_handler, **ssl_args)
.............
server.start() #启动server,开始等待链接,服务链接
servers.append(server)
.........
上面代码中的server_class实际上是一个gevent的WSGI SERVER的子类:
class PyWSGIServer(pywsgi.WSGIServer):
base_env = BASE_WSGI_ENV
需要注意的是构造PyWSGIServer的参数:
self.server_class(
s, application=self.wsgi, spawn=pool, log=self.log,
handler_class=self.wsgi_handler, **ssl_args)
这些参数中s是server用来监听链接的套接字。spawn是gevent的协程池。application即是我们的wsgi app(通俗点讲就是你用 flask 或者 django写成的app),我们的app就是通过这种方式交给gunicorn的woker去跑的。 handler_class是gevent的pywsgi.WSGIHandler子类。
当所有server对象创建完毕后,worker需要定时通知manager,否则会被认为是挂掉了。
while self.alive:
self.notify()
.......
这个地方的notify机制设计的比较有趣,每个worker有个与之对应的tmp file,每次notify的时候去操作一下这个tmp file(比如通过os.fchmod),这个tmp file的last update的时间戳就会更新。而manager则通过检查每个worker对应的temp file的last update的时间戳,来判断这个进程是否是挂掉的。
WSGI SERVER
真正等待链接和处理链接的操作是在gevent的WSGIServer 和 WSGIHandler中进行的。
最后再来看一下gevent的WSGIServer 和 WSGIHandler的主要实现:
WSGIServer 的start函数里面调用start_accepting来处理到来的链接。在start_accepting里面得到接收到的套接字后调用do_handle来处理套接字:
def do_handle(self, *args):
spawn = self._spawn
spawn(self._handle, *args)
可以看出,WSGIServer 实际上是创建一个协程去处理该套接字,也就是说在WSGIServer 中,一个协程单独负责一个HTTP链接。协程中运行的self._handle函数实际上是调用了WSGIHandler的handle函数来不断处理http 请求:
def handle(self):
try:
while self.socket is not None:
result = self.handle_one_request()#处理HTTP请求
if result is None:
break
if result is True:
continue
self.status, response_body = result
self.socket.sendall(response_body)#发送回应报文
..............
在handle函数的循环内部,handle_one_request函数首先读取HTTP 请求,初始化WSGI环境,然后最终调用run_application函数来处理请求:
def run_application(self):
self.result = self.application(self.environ, self.start_response)
self.process_result()
在这个地方才真正的调用了我们的 app。
总结:gunicorn 会启动一组 worker进程,所有worker进程公用一组listener,在每个worker中为每个listener建立一个wsgi server。每当有HTTP链接到来时,wsgi server创建一个协程来处理该链接,协程处理该链接的时候,先初始化WSGI环境,然后调用用户提供的app对象去处理HTTP请求。
安装Gunicorn
方式一:最简单的使用 easy_install 安装或者更新
pip install gunicorn
方式二:下载源码安装
1 git clone git://github.com/benoitc/gunicorn.git
2 cd gunicorn
3 sudo python setup.py install
如果想让Gunicorn支持异步 workers 的话需要安装一下三个python包
1 easy_install -U greenlet
2 easy_install -U eventlet
3 easy_install -U gevent
说明:如果安装 greenlet 失败的话,你需要安装 Python headers
1 sudo apt-get install python-dev
gunicorn还需要库函数 libevent(1.4.x or 2.0.4)
运行Gunicorn
成功安装 gunicorn 之后有以下三个指令你可以直接使用,用来启动 gunicorn 运行 wsgi application或者 wsgi frameworks
1 gunicorn
2 gunicorn_django
3 gunicorn_paster
Gunicorn server的最基本的命令,直接用来运行最基本的 wsgi application 。用法:
1 gunicorn [OPTIONS] APP_MODULE
OPTIONS 可选参数 运行gunicorn的配置选项,后面会讲到。
APP_MODULE 指定 wsgi application文件,书写格式 $(MODULE_NAME):$(VARIABLE_NAME)。其中 module_name用来制定将要运行的 wsgi application文件,可是一个完整的点缀名。比如当前目录 myapp 目录下有个 Python 包 gunicorn_app, gunicorn_app包下有一个wsgi application文件 test.py 则 module_name可以直接写成 gunicorn_app.test。viriable_name表示在 module_name 文件中要调用的对象(是一个WSGI callable, 可以是一个函数,类详情参看WSGI规格说明书)名。
按照上面的例子,当前目录为 /home/user/myapp, myapp中有一个包 gunicorn_app,test.py代码如下:
def app(environ, start_response):
"""Simplest possible application object"""
data = 'Hello, World!\n'
status = '200 OK'
response_headers = [
('Content-type','text/plain'),
('Content-Length', str(len(data)))
]
start_response(status, response_headers)
return iter([data])
我们将要运行 test.py文件中的 app(当然名字由你决定,可以是myapp,demo等等)
1 gunicorn gunicorn_app.test:app
命令中 module_name 为 gunicorn_app.test ;viriable_name为 app。当然如果我们这样直接运行Gunicorn的话,Gunicorn的所有配置都是默认值,后面会讲到如何配置Gunicorn。
gunicorn_django
guniorn_django命令是用来将 Django app部署到 Gunicorn Server上的。
其实也非常简单,原理和 gunicorn一样,只是gunicorn_django做了特殊处理,使得更加适合Django
基本用法
gunicorn_django [OPTIONS] [SETTINGS_PATH]
SETTINGS_PATH django app中 settings.py文件所在的目录,不写的话默认在当前目录下查找。比如:
gunicorn_django用法适用以 Django 1.4 以前。对于Django 1.4的版本以后推荐使用 gunicorn 命令(强烈推荐)
1 django_admin.py startproject mysite
2 cd mysite
3 gunicorn mysite.wsgi:application
Gunicorn配置
Gunicorn从三个不同的地方读取配置信息。
第一个地方:从framework定义的配置信息中读取,目前只对 Paster 框架有效。
第二个地方:在命令行中定义,命令行中定义的配置信息将会覆盖掉框架中定义的相同的参数名的值。
最后:将所有的参数信息,放到一个文件中,只要是在命令行中可以定义的参数中,在配置文件中都可以定义。(是一个Python源文件,所以你就像在写Python代码一样)
第一个地方不不介绍了,不实用。重点介绍第二种和第三种,其实这两种方式都是相同的。
显示说有配置选项:
1 gunicorn -h
使用命令行配置:
在上面的 myapp 例子的基础上
1 gunicorn --workers=4 --bind=127.0.0.1:8000 myapp.test:app
上面的命令 启动 4个 workers ,绑定到 127.0.0.1:8000
配置文件 config.py 源码
1 import multiprocessing
2
3 bind = "127.0.0.1:8001"
4 workers = multiprocessing.cpu_count() * 2 + 1
使用配置文件启动Gunicorn
1 gunicorn --config=config.py myapp.test:app
和上面用命令行配置的效果完全一样,当然两者还可以结合起来用:
1 gunicorn --config=gunicorn_conf.py --worker-class=eventlet myapp.test:app
worker-class默认是sync(同步),我们配置成了 eventlet(并发的)
正向代理服务器软件
反向代理服务器软件
TCP上的反向代理服务器软件
参考
http://gunicorn.org/
http://gunicorn.readthedocs.io/en/latest/