HTTP/1.1协议是一个基于文本的传输协议。传输报文都是直接以文本的形式传递消息。因此本质上讲,HTTP服务器就是负责解析文本,处理请求,而后组织文本并回传客户端。python
Web开发刚刚兴起的时候,HTTP服务器开发这块各家都有本身的实现,有本身的特色。有些报文解析速度快,有一些处理请求速度快,有一些组织回传结果的速度快。为了方便代码复用,实现这些不一样特色的服务器模块的按需组织,一些语言就自行定义了一些协议,对Web服务器的开发作出一些建议性的规定。好比,Java的servlet协议。git
Python则自定义了WSGI协议。github
Python官方在PEP-0333和PEP-3333中详细定义了WSGI协议的细节。前者主要适用于Python2.x,后者根据Python3.x的语言细节变化对WSGI协议作了一些调整。数据库
WSGI协议将整个Web服务器程序划分为应用框架端(后文简称为应用端)、服务器网关端(后文简称为服务器端)以及中间件。中间件,在应用端来看就是服务器端,在服务器端来看就是应用端。因此整体来说,WSGI就只规定了应用端和服务器端之间的交互协议。segmentfault
在WSGI协议中,服务器端负责监听套接字端口,接收报文,解析报文,向应用端发送解析结果,并接收应用端的响应结果,组织响应报文,向客户端发送响应报文。而应用端,接收到服务器端的解析结果后,根据HTTP方法、URI以及其余报文,执行代码,调用模版生成动态网页,也许会向数据库后端发送查询请求。后端
WSGI协议的交互核心就是一个app(environ, start_response)
,是一个回调机制的实现。app()
方法由应用端实现,environ
参数是标准的Python built-in字典,其中包含了WSGI环境变量以及服务器对HTTP请求报文的解析结果;start_response
是一个相似于函数的可调用对象(任何实现了__call__
方法的实例都是可行的),由服务器端实现,负责组织响应报文。start_response
接收两个参数status
, response_headers
。前者是应用端响应结果的状态,例如"100 Continue"
、"200 OK"
、"404 Not Found"
;后者这是响应报文的报文头,是一个数组,其中的每个元素又是一个元组,例如[("Content-type", "text/plain")]
。数组
WSGI协议规定的是一个同步Web框架,由于要照顾某些没法异步实现的流程。服务器
服务器端框架直接处理网络IO,对整个Web服务器的性能影响很是大。常见的服务器端框架有多线程、异步两种基础机制。网络
bjoern是一个使用C语言实现的内存占用少并且拥有极强性能的Python服务器端框架。bjoern使用了事件驱动框架libev、高性能HTTP解析框架http-parser。bjoern使用了Python2的C语言API,所以只能用于CPython解释器,且不支持Python3。原做者暂时也没有支持Python3的打算,issue: Python 3 support (PEP 3333) #29。此外bjoern不支持SSL。多线程
bjoern的异步机制是这样的。首先注册一个ev_io事件保持对主端口(80或者443)的监听状态。一旦套接字可读,表示有新的链接请求,这时候当即调用回调函数,执行accept动做,并注册另外一个ev_io事件保持对客户端链接的监听,并持续接收报文(io可读),调用WSGI协议,持续回传响应(io可写)等等。接收、回传两部均实现事件驱动机制。
同一个ev_loop,既保持对主端口的监听,同时保持对全部客户端口的报文收发,对其中的全部网络IO事件协调调度。
CherryPy是一个纯Python实现的生产级高稳定高性能框架,包含了应用端和服务器端。而cheroot则是CherryPy框架解耦以后分离出来的服务器端框架,支持Python2和Python3,同时还支持PyPy。
cheroot的多线程机制简单的说就是一个线程池。线程池使用数组维护。其中的线程进行了自定义,能够实现线程的状态查询和管理。cheroot有一个主线程监听经常使用端口,主线程会将新的链接请求放入一个任务队列当中。线程池中的线程从任务队列中获取任务并执行其他流程。这实际上是一个经典的“生产者-消费者”模式。
CPython和PyPy都有GIL,因此线程池是一个相对低效的实现。不管是内存占用仍是逻辑流的切换开销,多线程机制远比事件驱动机制的开销要大。
网上有一篇博文对几个Python服务器端框架进行了评测:A Performance Analysis of Python WSGI Servers: Part 2,能够进行一下简单的分析。