Python WSGI 详解

1. WSGI概述

2. WSGI相关类图详解

2.1 Server类

2.2 RequestHandler类

2.3 Handler类

2.4 小结

3. WSGI application

4. start_response疑惑

1. WSGI概述

WSGI不是服务器,也不是用于与程序交互的API,更不是真实的代码,而只是定义的接口,其目标是在WEB服务器与WEB框架层之间提供一个通用的标准,减少之间的互操作性并形成统一的调用方式。

那么这个接口到底是什么呢?

根据WSGI的定义,其应用application是可调用的对象,其参数固定为两个:

一个包含所有环境变量的dict对象environ;

另一个也是可调用对象,该对象使用HTTP状态码和返回客户端的HTTP头来初始化响应,同时返回一个可迭代对象的用于组成响应负载。

在这里从一段具体的代码进行讲解,学过python web都知道用下面的这段代码就可以产生一个web应用。

当用浏览器访问http://localhost:8000时会在浏览器看到Hello World字样。

下面将讲解WSGI所涉及的个各类

2. WSGI相关类图详解

wsgiref模块是Python内置了一个WSGI服务器,其方法make_server返回一个包含application的web server,make_server通过传入的server_class(这里默认是WSGIServer)创建一个web server,并设置web server关联的applicaion。咦,这里还有个默认参数handler_class是干嘛用的?我们通过代码一步步分析。

2.1 Server类

首先make_server会调用WSGIServer自socketserver.TCPServer继承的__init__创建实例,这过程会做这几步事情:

创建socket:调用socker.socket()方法

Socket绑定host,port:调用WSGIServer类的server_bind()

建立基本的environment:调用WSGIServer类的setup_environ()

Socket监听端口:调用TCPServer类的server_activate(),其实质也是调用socket.listen()。

以上几步实质上是socket的网络编程,用来建立TCP连接。

注:BaseServer里__init__( self, server_address, RequestHandlerClass)传入的RequestHandlerClass就代表着刚才make_server函数传入的参数

handler_class=WSGIRequestHandler。

5.紧接着设置WSGIServer的application,然后调用的httpd.serve_forever(),它本质上是一个select的I/O模型,当有请求来临时调用_handle_request_noblock()函数来处理请求。

6. _handle_request_noblock()调用get_request()函数获得请求内容request和客户端IP、Port信息client_address,紧接着会调用process_request处理请求

7.process_request会通过调用finish_request()会初始化一个RequestHandlerClass的实例,实质是make_server()函数传入的handler_class=WSGIRequestHandler的作用;这里是调用继承自BaseRequestHandler的__init__()方法。

2.2 RequestHandler类

8.这个初始化BaseRequestHandler过程会调用setup(),setup()函数中最后两行代码要注意,它们的作用是可以像读写文件那样读写socket。

注:SocketServer.StreamRequestHandler中对客户端发过来的数据是用rfile属性来处理的,rfile是一个类file对象.有缓冲.可以按行分次读取;发往客户端的数据通过wfile属性来处理,wfile不缓冲数据,对客户端发送的数据需一次性写入

(引用自http://a564941464.iteye.com/blog/1170464)

9.通过覆盖了的handle()请求进行处理

10.handler()会调用parse_request(),parse_request()代码比较长,这里就不贴出来了,有兴趣的童鞋去trace下代码,它的大致作用是解析http版本,method,path,Conntype(keep-alive)等,如果解析出错会返回False。如果成功,继续执行第11步

2.3 Handler类

11.handler()会调用handler=ServerHandler(self.rfile,stdout, self.get_stderr(), self.get_environ() ),

最后一个参数是通过get_environ()函数获取的,它会将调用WSGIRequestHandler的get_environ(),将3步和第10步的信息放到environ中,也就是最初application(environ,start_response)中传入的environ;然后生成了ServerHandler的实例handler

12.调用handler.run(self.server.get_app());self.server.get_app()是获取我们自己所写的application。

13.调用setup_environ()将WSGI服务器的一些信息放到environ中,这样就将所有的requset信息、server信息保存到environ中。

14.将environ和start_respnse()传给application

15.application调用start_response(),将status,header保存在BaseHandler实例中,并返回一个write函数

16.application将response body返回赋给result变量

17.调用finish_response()将response返回给客户端;我们看到finish_response函数体内是调用write函数,在发送body之前,要判断是否有status值,然后要判断header发出去的吗?如果没有则调用self.send_headers(),最后再发送response body。

2.4 小结

Server类建立基本的Socket连接,然后将请求发送给RequestHandle处理

RequestHandler类解析客户端的Request请求

Handler类设置server、request信息,并交给application

application进行业务的处理,然后由Handler发送response给客户端。

3. WSGI application

1)application必须是一个可调用对象,可调用对象可以是:

函数

具有__call__方法的实例

2)同时返回一个可迭代对象

3)接受environ和start_response两个参数

符合上述三点的就是一个WSGI标准的appication,因此这个application可以有以下几种形式

可调用函数

def application(environ, start_response):

return [b'Hello World',]

可调用类

class Application:

def__init__(self,environ, start_response):

pass

def__iter__(self):

yieldb'Hello World'

可调用实例

class ApplicationObj:

def__call__(self,environ, start_response):

return[b'Hello World',]

4. start_response疑惑

WSGI标准规定,start_respnsoe必须返回一个可调用对象,第15步提到start_response返回了一个wirte()函数,于是我们的application还可以这样写

def application(environ,start_response):

write=start_response('200 OK', [('Content-Type','text/html')])

write(‘HelloWorld!’)

return 'What?'

通过浏览器访问得到下面的结果

其实start_response根本没有必要这样,它只要把设置status、header就好了,至于返回response的事情还是让Server去干吧

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181205G1N4IX00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券