0. 前言
基本概念:
WSGI:全称是Web Server Gateway Interface,WSGI是一种规范,用来描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有 Flask, Django等。
WSGI协议主要包括server和application两部分:
-
WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端; -
WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序(执行程序),对应用程序来说,中间件扮演服务器(WSGI服务器)。
WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django,Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。
注:贴图代码都只是截取了关键入口的那部分关键语句和函数方法, 其他细节可看详细代码,我们把主流程走一遍就行了 。
一. 程序入口:runserver命令
python manage.py runserver 0.0.0.0:8000
注:runserver文件里的Command类继承了Django的BaseCommand,从而可以通过python manage.py <文件名> 运行该文件,命令运行的函数为handle()
文件地址:
/<我自己的虚拟环境地址>/lib/python3.7/site-packages/django/core/management/commands/runserver.py
注:可以找到文件从这里开始查看源码
- 执行handle()时调用run()方法:
- 执行run()时调用inner_run()方法:
- 执行inner_run()时调用WSGI的入口函数run():
二. WSGI 入口:basehttp模块
注:由于inner_run()时调用了basehttp模块的WSGI服务入口函数run(),执行的文件变成了basehttp.py(文件名即模块名)
文件地址:
/<我自己的虚拟环境地址>/lib/python3.7/site-packages/django/core/servers/basehttp.py
- 执行WSGIServer实例化对象的server_forever()方法(该方法继承自socketserver模块的BaseServer类),forever代表持续监听的意思,代码中有一个while语句(while not self.__shutdown_request:),意思是在请求处理完毕或者处理出现异常时,继续保持监听。代码如下:
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
if threading:
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
# 实例化WSGIServer,即WSGI协议的server部分
# 传入了WSGIRequestHandler参数,会在WSGIServer内部调用来处理请求
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
httpd.daemon_threads = True
# 设置WSGI协议的web application部分,即WSGIHandler
httpd.set_app(wsgi_handler)
# 启动监听,该方法继承自父类BaseServer
httpd.serve_forever()
三. WSGI 协议server 部分:
server部分主要分别由以下三个类(皆继承自python自带类)依次处理,将http请求转化为app(如django)可以处理的符合WSGI协议的请求:
1. WSGIServer:
继承关系:
WSGIServer(
simple_server.WSGIServer(
HTTPServer(
socketserver.TCPServer(
BaseServer # serve_forever()方法继承自它
))))
- 它的实例化对象的serve_forever()方法作为程序入口
- 接收http请求
- 实例化WSGIRequestHandler
- 将接收的http请求传给WSGIRequestHandler对象;
2. WSGIRequestHandler:
继承关系:
WSGIRequestHandler(
simple_server.WSGIRequestHandler(
BaseHTTPRequestHandler(
socketserver.StreamRequestHandler(
BaseRequestHandler))))
- 它主要起承上(对接WSGIServer)启下(对接ServerHandler)的连接作用;
- 接收到WSGIServer传来的http请求并处理;
- 它实例化时会自动调用handle()方法,该方法中会实例化ServerHandler;
- 将自己处理好的请求传给ServerHandler对象;
3. ServerHandler:
继承关系:
ServerHandler(
simple_server.ServerHandler(
SimpleHandler(
BaseHandle
)))
- 它主要起WSGI server部分和WSGI application(简称app,如django)部分的对接作用;
- 接收来自WSGIRequestHandler对象处理好的请求,并将其组装成环境变量;
- 实例化WSGI application(WSGIHandler);
- 将配置好的环境变量和回调函数start_response()传递给app对象;
- 通过那个回调函数接收app的处理结果(无外乎对数据库进行增删改查并返回响应,也就是我们写的那些代码)
流程大概如下(盗个图,改了点):
接下来就是server部分的主要流程:
1. WSGIServer:
由于继承python自带的BaseServer类方法,所以文件地址:
/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socketserver.py
- 执行server_forever()时调用_handle_request_noblock()方法
- 实例化WSGIRequestHandler
- 注:WSGIServer实例化时传入了WSGIRequestHandler参数,如代码所示:httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
2. WSGIRequestHandler:
文件地址:
/<我自己的虚拟环境地址>/lib/python3.7/site-packages/django/core/servers/basehttp.py
- 在上面WSGIServer中WSGIRequestHandler已经实例化了,实例化时会自动调用handle()方法;
- 实例化时传入了三个参数,如上图,最后一个是‘self’,也就是WSGIServer对象(为什么我创建其他对象的时候还要把自己传给它呢?),留着,后面会用(也就是下图的‘server’):
- handle()方法被WSGIRequestHandler重写(其实代码一个字没变,就是复制了一份,因为在方法中实例化ServerHandler的类从python自带的simple_server模块中的ServerHandler变成了basehttp模块中的ServerHandler),用来处理单个http请求;
- ServerHandler实例化后,调用run()方法,这个run()方法就是我们WSGI server部分的最后一个类ServerHandler的入口:
3. ServerHandler:
由于继承python自带的BaseHandler类方法,所以文件地址:
/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/wsgiref/handlers.py
番外篇:
- 在上面实例化时,传入了一个参数,也就是‘self.server.get_app()’,你看,我就说那个‘server’留着有用吧,这个‘server’就是WSGIServer对象,它的get_app()方法可以获取到application(也就是WSGI application部分,后面简称app),而WSGI的入口,WSGIServer实例化后设置了application(httpd.set_app(wsgi_hxandler)),好了好了,怕你们忘了在哪儿了我再贴一次代码:
要是又有人问你怎么知道设置的这个 app是WSGIHandler?我...我......我.........就告诉你吧!!!????????????
还记得我们开天辟地的启动命令<python manage.py runserver 0.0.0.0:8000>吧!它给basehttp模块的run()函数传了一个handler,也就是上面截图中<set_app(wsgi_handler)>那个wsgi_handler:
这个handler参数又是通过命令中的get_handler()方法获取的:
真的是老母猪的胸罩一套有一套...这个<get_internal_wsgi_application()>继续追溯下去,不想说话了,直接上图:
这些找到这个WSGIHandler对象(注意是对象,不是类)了吧,就是我们的app。
言归正传:
- 我们上一步实例化了ServerHandler并执行了它的run(app对象)方法,调用app并返回<result>:
- 调用app对象时传入了两个参数, 一个是前面ServerHandler将http请求组装成的环境变量<environ>, 一个是作为app处理成功后的回调函数<start_response()>(题外话:这应该是阻塞的,只有app执行成功返回<result>之后才会执行<finish_response()>)
四. WSGI 协议app 部分:
我们知道了实例化ServerHandler对象的run()方法传入的是app对象(WSGIHandler对象),为何给对象传参数就会返回数据呢?
那就顺便巩固下基础(复制过来的):
__call__():Python中,只要在创建类型的时候定义了__call__()方法,这个类型就是可调用的。
Python中的所有东西都是对象,其中包括Int/str/func/class这四类,它们都是对象,都是从一个类创建而来的。元类就是创建这些对象的东西,type就是Python的内建元类。
其中,func是可调用的对象,说明在创建它的类型(父类或它本身)的时候,定义了__call__()方法。
原来是因为WSGIHandler类中定义了<__call__>呀!大彻大悟,看来基础薄弱简直是洪水猛兽啊!
那下面就来看看WSGIHandler中是如何定义<__call__>的:
- 解析环境变量(ServerHandler组装好的<environ>)
- 获取程序响应(也就是我们平时写的逻辑代码,当然中间还有很多中间价处理包装了的)
到这里我们的一个http请求主要的处理流程差不多就已经完了,可以不用往下看了,需要继续了解的如何返回响应的,那就往下瞧:
- 执行回调函数start_response(),看一下回调时执行了啥:上图可以看出,回调函数的返回值并没有被接收,只是像18岁少女一样单纯地执行了一下,我还没看代码,我是边看代码边写笔记的,我猜测是为了给响应铺平道路,把响应时需要的环境搭好,开始看代码:
看嘛!我就说嘛,红框中明显就是设置ServerHandler对象的属性,先把响应需要使用到的对象熟悉先配好,然后返回了self.write......这是个对象呀,也没有变量接收它呀,所以回调到这儿就没啥事儿了,shutdown。
另外通过注释<callable as specified by PEP 3333>可以看出,这是为了我们PEP 3333 -- Python Web Server Gateway Interface(顺便查了下)做特殊服务的。
- 返回响应:上面可知,ServerHandler对象的result属性接收了app的响应(一个HttpResponse object),然后执行finish_response()方法:
看看都做了哪些事:1. 把接收到的app响应<result> 中的数据循环遍历发送(通过write()方法)到客户端