WSGI是Web Server Gateway Interface的缩写。它是Python专有的一种接口规范(其它语言也有类似的规范,只是名字不一样而已,Python则是第一个提出该规范的语言)。该规范规定了WEB服务器与WEB应用框架之间的通信方式。
首先,WEB应用需要提供一个可调用的接口(如:函数);该接口接收2个参数:
其次,WEB服务器需要定义一个回调函数,该回调函数接收2个参数:
到目前为止,WSGI规范的说明就结束了,接着就是如何使用该规范。在日常的项目中,我们不会自己去写WSGI服务,也不会自己写WSGI的接口。因为这些都是比较公用的模块或框架,自然就有人已经写好了这些功能,我们只要直接使用即可。
首先,WEB应用需要提供一个可调用的接口(如:函数);该接口接收2个参数:
- 第一个参数是当前请求所有请求信息的字典对象
- 第二个参数是一个回调函数
其次,WEB服务器需要定义一个回调函数,该回调函数接收2个参数:
- 第一个参数是响应码
- 第二个参数是响应头的列表
- WEB服务器在接收到用户请求
- 组装本次请求的所有信息并存放在字典对象
- 调用WEB应用提供的接口,并传递响应的参数
- 从WEB应用接口获取返回的响应体内容
- 从传递的回调函数中获取响应码和响应头
总的来讲基本就是WEB服务器会把请求相关信息,通过调用接口的方式传递给WEB应用程序;接口WEB应用程序通过调用回调函数和返回值的方式,分别返回响应头和响应体内容给WEB服务器。其流程示意如下:
下面清单中是最简单的WSGI服务
#!/usr/bin/env python
#coding:utf-8
import socket
from app import app
g_status_code = g_rep_headers = None
def parse_headers(buf):
headers = {}
lines = buf.split('\r\n')
for line in lines[1:]:
line = line.strip()
if not line:
break
sp = line.split(':', 2)
headers[sp[0]] = sp[1]
return headers
def callback(status_code, rep_headers):
global g_status_code, g_rep_headers
g_status_code = status_code
g_rep_headers = rep_headers
def handle_request(client):
global g_status_code, g_rep_headers
buf = client.recv(1024)
headers = parse_headers(buf)
rsp = app(headers, callback)
headers = [":".join(header) for header in g_rep_headers]
client.send("%s\r\n%s\r\n\r\n" % (g_status_code, '\r\n'.join(headers)))
client.send(rsp)
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8080))
sock.listen(5)
while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ == '__main__':
main()
该清单中实现了一个socket服务,它监控了本地的8080端口,当有用户请求到达时则会在handle_request函数中,通过WSGI的方式调用外部的app接口,该接口会直接返回响应内容,并且会执行传递给它的call_back函数,最后handle_request函数中会把分散的信息组织起来,发送给访问用户。
其对应的app服务接口定义如下(保存为app.py文件):
#!/usr/bin/env python
#coding:utf-8
def app(environ, start_response):
##do some this with different path
data = b"Hello, World!\n"
start_response("200 OK", [
("Content-Type", "text/plain"),
("Content-Length", str(len(data)))
])
return iter([data])
该清单中只定义了一个app函数,也就是上一个清单中被引入的app接口。在app函数中可以根据不同的头信息来决定如何处理请求,最终返回对应的响应信息。
到目前为止,WSGI规范的说明就结束了,接着就是如何使用该规范。在日常的项目中,我们不会自己去写WSGI服务,也不会自己写WSGI的接口。因为这些都是比较公用的模块或框架,自然就有人已经写好了这些功能,我们只要直接使用即可。
首先,Python官方就自带了一个WSGI的服务,我们可以直接使用。方式如下:
#!/usr/bin/env python
#coding:utf-8
from wsgiref.simple_server import make_server
from app import app
httpd = make_server('localhost', 8080, app)
httpd.serve_forever()
该代码的功能与清单1基本相同,而我们只需要实现一个清单2中WSGI接口即可。
此外,还有很多的第三方WSGI库,比较流行的一个是gunicorn。官方给出的使用例子如下:
pip install gunicorn ##安装gunicorn库
gunicorn -w 4 app:app ##启动WSGI服务,并指定app接口所在位置,这里是app.py文件下的app函数,需要确保在app.py所在目录执行
是不是有点惊为天人,我们尽然一行代码都不需要多写就可以把现有的WSGI接口集成到WSGI服务中。并且-w 4是指启动4个worker进程来提供服务,也就是说我们的WSGI接口通过gunicorn启动之后,除了更方便之外,还同时支持了多进程的并发能力。
既然WSGI有第三方这么好的实现,那么WSGI接口呢?答案是当然有的。并且目前Python的大部分WEB框架都是支持WSGI接口的,比如:Django、Flask、Tornado等。所以当我们需要使用WSGI服务的时候,其实就是配置好这些模块并相应的启动即可。下面是以Flask为例启动的WSGI服务的样例:
#!/usr/bin/env python
#coding:utf-8
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
把上述清单保存到flask_demo.py中,然后在该文件所在目录执行如下命令:
gunicorn -w 2 flask_demo:app
该命令启动了2个gunicorn的worker来执行flask的app应用。