楔子

承接上文,今天讲讲python后端流量处理过程中第一个步骤:接收数据包。接收数据包一般需要web服务器来处理,常用的python web服务器可以是nginx,httpd, WSGI容器本身也可以做web服务,考虑到扩展,高可用,生产上一般常用nginx+gunicornuwsgi),通讯方式一般 unix socket.

今天的重点是WSGI容器 Gunicorn

image.png

知识准备

pre-forking

  • pre意思是在requests 到来之前,先fork出多个worker子进程;

  • worker负责处理请求,master负责管理worker的运行周期。

image.png

WSGI

全称Python Web Server Gateway Interface,指定了web服务器和Python web应用或web框架之间的标准接口,以提高web应用在一系列web服务器间的移植性。

从定义上讲,如下几点:

  • WSGI是一套接口标准协议/规范;

  • 通信(作用)区间是Web服务器和Python Web应用程序之间;

  • 目的是制定标准,以保证不同Web服务器可以和不同的Python程序之间相互通信,比如nginx+gunicorn+flask,或者nginx+uwsgi+django等等。但凡实现wsgi协议均可替换。

socket

传统意义上是类似于文件描述符的存在形式,是一种抽象的资源定位,即进程端口资源。

1,一个程序启动后,有pid文件标识锁定该运行的程序。
2,一个文件打开后,在进程中有一个fd标识着它。
3,一个进程占用了一个端口,并建立相关协议的通信,由socket标识着它。

所以socket就是一个资源标识,是一个三元组(协议,端口号,IP地址)。

基于socket的编程,指的是在该资源(三元组)的基础上,封装对数据的处理。就好比打开一个文件后,对文件的编程,如函数f.Close()。

socket编程必备的函数如下:

· Socket():创建一个socket。

· Bind():绑定地址,即该socket负责哪个资源。

· Listen():开始监听。

· Accept():接收请求。

· Connect():建立连接

· Recv()/Send():数据发送和接收

客户端->建立socket->Connect()建立连接->Close()。

服务端->建立socket->Bind()->Listen()->Accept()接收请求->Close()。

Gunicorn是什么

Gunicorn Green Unicorn是一个UNIXPython WSGI HTTP服务器。它是一个从RubyUnicorn项目移植过来的预分叉工作器模型。Gunicorn服务器广泛兼容各种web框架,实现简单,服务器资源少

Gunicorn实现了WSGI协议,所以它可以作为web服务器和程序框架之间的纽带,http报文转换为WSGI规定的格式。

gunicorn.png

项目中如何使用

  • 命令行模式

gunicorn -h 有很多参数,读者有兴趣可以细究下,本文主要讲原理实现。

image.png

  • 文件配置形式
[program:gunicorn_demo]
process_name=%(program_name)s
numprocs=1
priority=901
directory = /opt/gunicorn_demo/
command = /opt/virtualenv/bin/python /opt/virtualenv/bin/gunicorn -c gunicorn_demo.py gunicorn_demo:app
autostart = true
startsecs = 20
autorestart = true
startretries = 3
user = root
redirect_stderr = true
stdout_logfile_maxbytes = 20MB
stdout_logfile_backups = 10
stdout_logfile = /dev/null

工作原理

如下图:

gunicorn运行逻辑图,从图中我们可以看到,gunicorn主要做了如下几件事情:

  • 启动框架程序;

  • 接收数据包并转换wsgi格式,传递给框架;

  • 提供并发模型,管理进程.

image.png

gunicorn代码调用,最重要的逻辑在Arbiter类中,arbitor在计算机专业术语中叫 总线控制器,顾名思义,这个类很重要,gunicorn主要功能都围绕它展开。

image.png

gunicorn提供的并发模型(文件路径gunicorn/workers

同步模型Sync Workerssync.py

如下图是 同步worker的流程图,accept时,设置为阻塞模式。

image.png

并发模型如下:

image.png

同步worker一个进程里一个线程,线程按照顺序处理请求,后面的请求需要等待。

同步worker适合在访问量不大、CPU密集而非I/O密集的情形。

好处是一个worker进程crash了,也只会影响一个请求。

异步模型Async Workers(异步workers)

ggevent.py,geventlet.py

异步并发模型如下,异步worker也只有一个线程,但能同时处理不同请求,不会阻塞后面的请求

image.png

异步worker是怎样实现并发,使得一个worker就能同时处理很多请求的呢?

 

Gevent为例,每个请求的连接是一个Greenlet协程。Gevent虽然只有一个线程、同时只能处理一个请求,但是在这个请求的异步事件没准备好、进入IO等待时,能主动yield让出控制权、而不是阻塞其他请求的协程,而是先让其他协程执行,当自己的IO准备好时,事件循环会将它从yield让出控制权的地方,继续恢复执行。

 

这样,Gevent就能在不同请求间不断切换,从而实现并发,以充分利用CPU、减少IO等待。并且,因为切换的Greenlet微线程,它操作的维度是函数,而不是线程/进程,所以来回切换的开销,就没有那么大。一般来说,我们的web app多半属于外部IO密集型(总要访问db、访问第三方服务等等),所以用GunicornGevent异步worker(比如Gevent),就非常合理。而如果你的web appCPU密集型,或者你希望请求之间不要互相影响,那么可以选择Gunicorn的同步worker

这里有三个概念:greenlet,eventlet,gevent。

greenlet是Python的协程实现,可以理解为微线程。

Gevent是一个Python网络函数库,它通过Greenlet协程+libev快速事件循环,实现了异步模型。gevent的猴子补丁读者有兴趣可以了解下。

Tornado Workers

用来配合Tornado使用。Tornado是一个Python框架和网络库,可以提供异步IO非阻塞型模型,来处理长延时请求。

Tornado也是一个很有名框架,笔者也计划写Tornado源码系列文章。

 

多线程Workers

一个请求由一个线程处理,使用线程池技术。这里不展开,后续有线程池和进程池系列文章。

image.png