🤣

WSGI

介绍

notion image
WSGI是一套作用在web服务器和python web应用程序之间的的接口标准协议,目的是制定标准,以保证不同web服务器可以跟不同的python web应用程序之间进行通信
notion image

wsgi标准

web服务器将请求转交给web应用程序之前,需要先将http报文转化尾wsgi规定的格式;wsgi规定:web应用程序application必须有一个可调用的对象供server调用,server先收到用户的请求,然后调用application提供的可调用对象,调用的结果会被封装成http响应后发送给客户端

可调用对象

该可调用的对象接受两个参数,返回一个可迭代对象
  • environ:字典,包含请求的所有信息
首先是CGI规范中要求的变量:
REQUEST_METHOD: HTTP请求方法,'GET', 'POST'等,不能为空
SCRIPT_NAME: HTTP请求path中的初始部分,用来确定对应哪一个application,当application对应于服务器的根,可以为空
PATH_INFO: path中剩余的部分,application要处理的部分,可以为空
QUERY_STRING: HTTP请求中的查询字符串,URL中?后面的内容
CONTENT_TYPE: HTTP headers中的Content-Type内容
CONTENT_LENGTH: HTTP headers中的Content-Length内容
SERVER_NAME和SERVER_PORT 服务器域名和端口,这两个值和前面的SCRIPT_NAME, PATH_INFO拼起来可以得到完整的URL路径
SERVER_PROTOCOL: HTTP协议版本,'HTTP/1.0'或'HTTP/1.1'
HTTP_Variables: 和HTTP请求中的headers对应,比如'User-Agent'写成'HTTP_USER_AGENT'的格式
WSGI规范中还要求environ包含下列成员:
wsgi.version:一个元组(1, 0),表示WSGI版本1.0
wsgi.url_scheme:http或者https
wsgi.input:一个类文件的输入流,application可以通过这个获取HTTP请求的body
wsgi.errors:一个输出流,当应用程序出错时,可以将错误信息写入这里
wsgi.multithread:当application对象可能被多个线程同时调用时,这个值需要为True
wsgi.multiprocess:当application对象可能被多个进程同时调用时,这个值需要为True
wsgi.run_once:当server期望application对象在进程的生命周期内只被调用一次时,该值为True
  • start_response:在可调用对象中调用的函数,用来发起响应,参数包括状态码,header等
start_response是一个可调用对象,接收两个必选参数和一个可选参数:
- status: 一个字符串,表示HTTP响应状态字符串,比如'200 OK'、'404 Not Found'
- headers: 一个列表,包含有如下形式的元组:(header_name, header_value),用来表示HTTP响应的headers
- exc_info(可选): 用于出错时,server需要返回给浏览器的信息
start_response必须返回一个write(body_data)
我们知道HTTP的响应需要包含status,headers和body,所以在application对象将body作为返回值return之前,需要先调用start_response,将status和headers的内容返回给server,这同时也是告诉server,application对象要开始返回body了。
server端调用函数
def hello(environ, start_response): status = "200 OK" response_headers = [('Content-Type', 'text/html')] start_response(status, response_headers) path = environ['PATH_INFO'][1:] or 'hello' return [b'<h1> %s </h1>' % path.encode()]
该方法负责获取environ字典中的path_info,也就是获取请求路径,然后在前端展示。

启动WSGI服务器

# coding:utf-8 """ desc: WSGI服务器实现 """ from wsgiref.simple_server import make_server from learn_wsgi.client import hello def main(): server = make_server('localhost', 8001, hello) print('Serving HTTP on port 8001...') server.serve_forever() if __name__ == '__main__': main()

关系

由此可见,server负责接收HTTP请求,根据请求数据组装environ,定义start_response函数,将这两个参数提供给application。application根据environ信息执行业务逻辑,将结果返回给server。响应中的status、headers由start_response函数返回给server,响应的body部分被包装成iterable作为application的返回值,server将这些信息组装为HTTP响应返回给请求方。
在一个完整的部署中,uWSGI和Gunicorn是实现了WSGI的server,Django、Flask是实现了WSGI的application。两者结合起来其实就能实现访问功能。实际部署中还需要Nginx、Apache的原因是它有很多uWSGI没有支持的更好功能,比如处理静态资源,负载均衡等。Nginx、Apache一般都不会内置WSGI的支持,而是通过扩展来完成。比如Apache服务器,会通过扩展模块mod_wsgi来支持WSGI。Apache和mod_wsgi之间通过程序内部接口传递信息,mod_wsgi会实现WSGI的server端、进程管理以及对application的调用。Nginx上一般是用proxy的方式,用Nginx的协议将请求封装好,发送给应用服务器,比如uWSGI,uWSGI会实现WSGI的服务端、进程管理以及对application的调用。

uWSGI、uwsgi、与WSGI的区别:

uwsgi:与WSGI一样是一种通信协议,是uWSGI服务器的独占协议,据说该协议是fastcgi协议的10倍快。
uWSGI:是一个web server,实现了WSGI协议、uwsgi协议、http协议等。

Flask与WSGI

Flask中的程序实例app就是一个可调用对象,我们创建app实例时所调用的Flask类实现了__call方法,__call方法调用了wsgi_app()方法,该方法完成了请求和响应的处理,WSGI服务器通过调用该方法传入请求数据,获取返回数据:
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) error = None try: try: ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error) def __call__(self, environ, start_response): return self.wsgi_app(environ, start_response)