什么是Gunicorn?
Gunicorn,是一个针对Python的、在Unix系统上运行的、用来解析HTTP请求的网关服务。
它的特点是:能和大多数的Python web框架兼容;使用简单;轻量级的资源消耗;高性能。
先来看官方定义:a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model.
Q1:什么是WSGI HTTP Server?
A1:关键词在WSGI(Web Server Gateway Interface)。它不是web server,也不是web app;而正是为了将web和app解耦、再连接起来,这样的一道桥梁。因为它是一种通用接口规范,规定了web server(如Apache、Nginx)和web app(或web app框架)之间的标准。
有了它,web app开发者就能专注于业务逻辑、专注于HTML文档的生成,而不用操心繁琐的网络底层实现(HTTP请求接收、建立连接、返回响应等),并能方便地组合搭配不同的web server + web app/框架了。
Q2:什么是pre-fork worker model?
A2:“worker model”意味着:这个模型有一个master进程,来管理一组worker进程;“fork”意味着:worker进程是由master进程fork(复刻)出来的;“pre-”意味着:在任何客户端请求到来之前,就已从master进程fork出了多个worker进程,坐等请求到来。
在worker进程创建时,就被实例化了Python web app;并由worker进程监听端口、处理请求。那么,当请求到来时,worker进程就能解析HTTP请求、调用Python web app处理、得到处理结果后,再整理成HTTP Response,通过TCP返回给客户端。
而master进程是不管处理请求的,只负责管理worker进程,比如对worker进程的创建、销毁、以及根据负载情况增减。(启动时设置的--workers参数只是worker数,而Gunicorn还会创建个master进程。所以,即使配置workers为1,你的app也至少有俩进程:master负责管理,worker负责处理请求。)
(当然了,Gunicorn是WSGI的实现,但同时也自带web server,能直接对外提供web服务。包括大部分的web app框架比如Flask和Django也都带有web server。不过,在真正的生产环境的部署中,它们还是各司其职,Flask/Django只用于写app、Gunicorn只用于运行和管理Python web app,而在它们前面有专门的web server,比如Nginx。)
怎样安装和使用Gunicorn?
安装(e.g.,用Python包管理工具pip安装):
$ pip install gunicorn
使用
起来后,Gunicorn的所有worker共用一组listener(Gunicorn支持绑定多个socket,所以说是一组)。在启动worker时,worker内为每个listener创建一个WSGI server,接收HTTP请求,并调用app对象去处理请求。
Gunicorn支持哪些worker进程?
Gunicorn支持使用不同的worker进程类型,可通过worker-class参数配置。
1. Sync Workers(同步workers)
默认的、最简单的worker模式,是同步模式。
每个worker进程,一次只处理一个请求;如果此时又有其他请求被分配到这个worker进程,那只好被阻塞了,要先等第一个请求完成 。并且,一个请求一个进程,并发时,显然很占CPU和内存。
因此,只适合在访问量不大、CPU密集而非I/O密集的情形。
不过也不是没有好处;好处是,即使一个worker进程crash了,也只会影响一个请求。
2. Async Workers(异步workers)
有Gevent和Eventlet两种,都是基于Greenlet实现的。
用了异步worker,就能同时处理不止一个请求,就不会出现上面同步worker那样,一个请求就把后续请求都block阻塞住的情况了。
Q1:什么是Greenlet?
A1:Python的协程实现;可以理解为微线程。(协程是program级的“线程”,而真正的线程是OS级的。)
不同的Greenlet只能在同一个线程内的不同代码段间切换;同一个线程里,每次也只能有一个Greenlet在run。Greenlet的调度也不由系统完成,而是在用户层面实现,因而其切换的开销比线程要小。
Q2:什么是Gevent?
A2:Gevent是一个Python网络函数库,它通过Greenlet协程+libev快速事件循环,实现了异步模型。
有了Gevent,切换Greenlet时就不再需要手动切换,而是当一个Greenlet遇到I/O时,Gevent能自动切换Greenlet,保证总有Greenlet在运行,而无需等待I/O。
Gevent也有自带的WSGI Server可使用(gevent.pywsgi),但它就没有多进程的能力了,除非启动服务时,以multiprocessing的方式。
(顺便一提,Gevent最好的地方在于,当你的web app是同步处理请求的,而你又需要赋予它异步能力,那么不需改代码、只需打个猴子补丁monkey patch,Gevent就会帮你改造Python标准库和一些第三方库,使你具备异步处理请求的能力。)
异步worker是怎样实现并发,使得一个worker就能同时处理很多请求的呢?
以Gevent为例,每个请求的连接是一个Greenlet协程。Gevent虽然只有一个线程、同时只能处理一个请求,但是在这个请求的异步事件没准备好、进入IO等待时,能主动yield让出控制权、而不是阻塞其他请求的协程,而是先让其他协程执行,当自己的IO准备好时,事件循环会将它从yield让出控制权的地方,继续恢复执行。
这样,Gevent就能在不同请求间不断切换,从而实现并发,以充分利用CPU、减少IO等待。并且,因为切换的Greenlet是“微线程”,它操作的维度是函数,而不是线程/进程,所以来回切换的开销,就没有那么大。
就我个人理解,同步worker和Gevent异步worker,这两种worker类型是最常用的。一般来说,我们的web app多半属于外部IO密集型(总要访问db、访问第三方服务等等),所以用Gunicorn的Gevent异步worker,就非常合理。
而如果你的web app是CPU密集型,或者你希望请求之间不要互相影响,那么可以选择Gunicorn的同步worker。
3. Tornado Workers
用来配合Tornado使用。
Tornado是一个Python框架和网络库,可以提供异步IO非阻塞型模型,来处理长延时请求。
3. AsyncIO Workers
分成gthread和gaiohttp两种模式。
gaiohttp利用aiohttp库,在服务端和客户端执行异步IO操作。支持web socket。gthread是一种全线程worker,worker与线程池保持连接,线程会等待接收请求,一个请求一个线程。在Gunicorn启动时,除了可配置worker进程数,还可以配每个进程里的thread线程
Gunicorn是怎样实现高并发的?
上面提过,Gunicorn启动时,就把worker进程预先fork出来了。当多个请求到来的时候,会轮流复用这些worker进程,从而能提高服务器的并发负载能力。
至于worker数的配置,一般推荐2*CPU数+1。这样推荐,背后的想法是,在任何时间,都有大概一半的worker是在做I/O,剩下一半才是需要CPU的。
如果在开多进程的同时,也开多线程(也就是选择gthread类型的worker),那么,配置总的并发数(worker进程数*线程数),仍然建议2*CPU数+1。