0.1 WSGI概念
出自python的增强性建议书:PEP-3333,由PEP-333发展而来(为了支持python3)全称Web Server Gateway Interface
在python中有各种web应用框架,不同的应用框架会限制使用他们的web服务器,相比于JAVA,它虽然也有众多的web开发框架,但自从servlet API出现之后,JAVA web框架都可以在支持servlet API的web服务器上运行。WSGI协议也就是充当了srvlet API这样的一个角色,它定义了应用或框架和web服务器之间通信的接口,使得python的web框架可以在任何支持WSGI协议的web服务器上运行。
那到底开发遵循WSGI协议去开发框架、应用、web服务器等带给我们什么好处呢
至少有了一个明确的领域划分,我们不需要在开发一个web应用或框架的同时还要去想着去实现一遍web服务器的功能,专注各自的领域,减少重复造轮子。
在就是移植性强,项目灵活,我们不要再去考虑说只能在项目里使用一种web框架,当我们的web服务器遵守了WSGI协议,应用层的框架选择不在是问题。
0.2 WSGI协议内容
WSGI协议把整个web服务端分为三个部分,Server、Application、Middleware。
0.2.1. Server
Server端每次从http客户端收到一个请求,就调用一次应用对象。需要实现的是一个将请求中包含的参数、请求头、元数据写入到一个字典中,和一个返回数据给客户端的函数,一并传入到Application中。
这个Server才是我们处理所有web请求的关键所在,对外需要去处理并发请求,对类则是加载我们的应用代码处理每个请求的逻辑
下面用python实现了一个cgi进程,通过环境变量获取一个请求的参数,并用cgi进程处理请求输出到标准输出。
import os, sys
enc, esc = sys.getfilesystemencoding(), 'surrogateescape'
def unicode_to_wsgi(u):
# Convert an environment variable to a WSGI "bytes-as-unicode" string
return u.encode(enc, esc).decode('iso-8859-1')
def wsgi_to_bytes(s):
return s.encode('iso-8859-1')
def run_with_cgi(application):
environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}
environ['wsgi.input'] = sys.stdin.buffer
environ['wsgi.errors'] = sys.stderr
environ['wsgi.version'] = (1, 0)
environ['wsgi.multithread'] = False
environ['wsgi.multiprocess'] = True
environ['wsgi.run_once'] = True
if environ.get('HTTPS', 'off') in ('on', '1'):
environ['wsgi.url_scheme'] = 'https'
else:
environ['wsgi.url_scheme'] = 'http'
headers_set = []
headers_sent = []
def write(data):
out = sys.stdout.buffer
if not headers_set:
raise AssertionError("write() before start_response()")
elif not headers_sent:
# Before the first output, send the stored headers
status, response_headers = headers_sent[:] = headers_set
out.write(wsgi_to_bytes('Status: %s\r\n' % status))
for header in response_headers:
out.write(wsgi_to_bytes('%s: %s\r\n' % header))
out.write(wsgi_to_bytes('\r\n'))
out.write(data)
out.flush()
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if headers_sent:
# Re-raise original exception if headers sent
raise exc_info[1].with_traceback(exc_info[2])
finally:
exc_info = None # avoid dangling circular ref
elif headers_set:
raise AssertionError("Headers already set!")
headers_set[:] = [status, response_headers]
# Note: error checking on the headers should happen here,
# *after* the headers are set. That way, if an error
# occurs, start_response can only be re-called with
# exc_info set.
return write
result = application(environ, start_response)
try:
for data in result:
if data: # don't send headers until body appears
write(data)
if not headers_sent:
write('') # send headers now if body was empty
finally:
if hasattr(result, 'close'):
result.close()
0.2.2. Application
应用就是一个简单的接受两个参数的可调用对象,可以是函数,方法,类,实现了call的实例,该应用对象必须可以被多次调用,web服务器会重复的调用它。
基本结构:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Hello World!']
下面是Application两种实现:函数和类
def simple_app(environ, start_response):
"""最简单的应用对象"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return b"Hello world!\n"
class AppClass:
"""
生成一个AppClass的实例对象,那是一个生成器对象,当我们遍历这个对象,就会执行
__iter__方法,来达到重复执行的效果。当然我们想通过执行这个实例对象来执行,可以去 实现__call__方法。
"""
def __init__(self, environ, start_response):
self.environ = environ
self.start = start_response
def __iter__(self):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
self.start(status, response_headers)
yield b"Hello world!\n"
0.2.3. Middleware
中间件是是一个可以与两端交互的组件,也可看做是一个Application,它接受一个Application作为参数,并返回一个Application,这正是利用了Application的可嵌套性,用法类似app = mw1(mw2(app)),常见用法
-
重写environ,然后基于 URL,将请求对象路由给不同的应用对象
-
支持多个应用或者框架顺序地运行于同一个进程中
-
通过转发请求和响应,支持负载均衡和远程处理
-
支持对内容做后处理(postprocessing)
0.3. WSGI协议的一些延伸
0.3.1 关于uWSGI和uwsgi
uWSG一个web服务器,它实现了WSGI接口。
而uwsgi是一种二进制协议,用于两个web服务器用来通信,常见是使用nginx和uWSGI一起部署,nginx是uWSGI之间通讯使用uwsgi协议,而nginx就负责把http协议包转换成uwsgi协议包。
当然uWSGI是可以不依赖于nginx的,但客户端到服务端通常用的都是http协议,那么在uWSGI服务器就有两种选择:自己将http协议解析成uwsgi协议,它起了一个http进程接受客户端请求并解析然后用uwsgi协议传递到每个uWSGI服务器的work;另外一种就是整个过程都用http协议流通
0.3.1 wsgi应用的参数envrion
其实就是在遵循wsgi协议的server传递给application所需要的所有请求内容一个字典,里面包含了一个http请求的请求头、路由、请求体等参数
0.3.2 关于uWSGI和uwsgi
中文翻译文档:[https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Options.html](https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Options.html)
当然uWSGI是可以不依赖于nginx的,但客户端到服务端通常用的都是http协议,那么在uWSGI服务器就有两种选择:自己将http协议解析成uwsgi协议,它起了一个http进程接受客户端请求并解析然后用uwsgi协议传递到每个uWSGI服务器的work;另外一种就是整个过程都用http协议流通。
而uwsgi是一种二进制协议,用于两个web服务器用来通信,常见是使用nginx和uWSGI一起部署,nginx是uWSGI之间通讯使用uwsgi协议,而nginx就负责把http协议包转换成uwsgi协议包。
uWSGI是一个web服务器,它实现了WSGI接口。
这是我在知乎发的第一篇原创文章,把我以前的学习笔记汇总,加入了自己的理解,希望对后来人有帮助吧。