Python Web开发中,WSGI协议的作用和实现原理详解

时间:2024-07-29 15:03:20

首先理解下面三个概念:

WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范。

uwsgi:与WSGI一样是一种协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快。

uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。

WSGI协议主要包括server和application两部分:

其中application部分:

示例代码:

def application(environ, start_response):
status = '200 OK'
response_headers = [('Content-Type', 'text/html')]
# application内部调用start_response
start_response(status, response_headers) # 解析environ信息,业务逻辑处理后,返回对应的响应信息(下面代码只是模拟业务处理)
file_name = environ['PATH_INFO']
if file_name == "/index.py":
return index(file_name)
elif file_name == "/center.py":
return center(file_name)
else:
return 'Hello world a simple WSGI application!'

有了WSGI,我们在框架中只关心如何从environ这个dict(wgsi规定传过来一个字典)对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。

整个application()函数本身没有涉及到任何解析HTTP的部分。 也就是说,底层代码不需要我们自己编写,我们只负责业务逻辑部分即可。

但是我们想一下,这个application()函数怎么调用?如果我们自己调用,两个参数environ和start_response我们没法提供,返回的str也没法发给浏览器。

截取server端部分代码实例如下:

          # 重点关注逻辑
# 准备一个字典,里面存放需要传递给web框架的数据
env = dict()
# ----------更新---------
env['PATH_INFO'] = file_name # 例如 index.py(模拟请求信息) # 重点关注逻辑
# 服务器调用框架中实现的application函数,并将包含请求信息的字典和获取响应头的函数传入
response_body = self.application(env, self.start_response)
​ # 合并header和body(响应客户端的请求,这个逻辑不用关注)
response_header = "HTTP/1.1 {status}\r\n".format(status=self.headers[0])
response_header += "Content-Type: text/html; charset=utf-8\r\n"
response_header += "Content-Length: %d\r\n" % len(response_body.encode("utf-8"))
for temp_head in self.headers[1]:
response_header += "{0}:{1}\r\n".format(*temp_head)

response = response_header + "\r\n"
response += response_body

client_socket.send(response.encode('utf-8'))

# 重点关注逻辑
# server端实现了start_response函数的定义
def start_response(self, status, headers):
"""这个方法,会在 web框架中被默认调用"""
response_header_default = [
("Data", time.time()),
("Server", "ItCast-python mini web server")
]

# 将状态码/相应头信息存储起来
# [字符串, [xxxxx, xxx2]]
self.headers = [status, response_header_default + headers]

这段代码,是在客户端请求动态资源时启动,即服务器需要向后端框架请求资源。代码解析已在代码中注释。

所以我们基本可以获得如下结论:

要实现WSGI协议,必须同时实现web server端和web application端

当前运行在WSGI协议之上的web框架有Torando,Flask,Django等

比较常用的WSGI协议服务器有:uWSGI,gunicorn等

1、框架需要实现WSGI协议,例如:Flask,Django等

2、environ 和 start_response 由 http server 提供并实现

3、environ 变量是包含了请求信息的字典

4、Application 内部在返回前调用 start_response

5、application()函数必须由WSGI服务器来调用

server要履行的任务:

1、接收HTTP请求

2、解析HTTP请求

3、准备 environ请求参数

4、定义 start_response 函数

5、组装响应头和相应体返回给客户端

application要履行的责任:

1、解析服务器发来的请求信息

2、根据请求信息,进行业务处理

3、返回所需要的数据

至此,您应该对WSGI有了一定的了解吧。

扩展阅读:

目前一些主要的实现了WSGI协议的服务器:

gunicorn

Gunicorn(从Ruby下面的Unicorn发展而来):依赖Nginx的代理行为,同Nginx进行功能上的分离。由于不需要直接处理用户来的请求(都被Nginx先处理),Gunicorn不需要完成相关的功能,其内部逻辑非常简单:接受从Nginx来的动态请求,处理完之后返回给Nginx,由后者返回给用户。

由于功能定位很明确,Gunicorn得以用纯Python开发:大大缩短了开发时间的同时,性能上也不会很掉链子。同时,它也可以配合Nginx的代理之外的别的Proxy模块工作,其配置也相应比较简单。

配置上的简单,大概是它流行的最大的原因。

uWSGI

因为使用C语言开发,会和底层接触的更好,配置也是比较方便,目前和gunicorn两个算是部署时的唯二之选。

以下是通常的配置文件

fcgi

不多说,用的少。

bjoern

Python WSGI界最牛逼性能的Server其中一个是bjoern,纯C,小于1000行代码,就是看不惯uWSGI的冗余自写的。