内容简介:WSGI 是 Python Web 开发中经常提到的名词,在维基百科中,定义如下:Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口。正如定义,WSGI 不是服务器,不是 API,不是 Python 模块,而是一种规定服务器和客户端交互的
WSGI 是 Python Web 开发中经常提到的名词,在维基百科中,定义如下:
Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为 Python 语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口。
正如定义,WSGI 不是服务器,不是 API,不是 Python 模块,而是一种规定服务器和客户端交互的 接口规范 。
WSGI 目标是在 Web 服务器和 Web 框架层之间提供一个通用的 API 标准,减少之间的互操作性并形成统一的调用方式。根据这个定义,满足 WSGI 的 Web 服务器会将两个固定参数传入 WSGI APP:环境变量字典和一个初始化 Response 的可调用对象。而 WSGI APP 会处理请求并返回一个可迭代对象。
WSGI APP
根据定义,我们可以实现一个非常简单的满足 WSGI 的 App:
def demo_wsgi_app(environ, start_response): status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) yield "Hello World!"
可以看到,该 App 通过 start_response
初始化请求,并通过 yield 将 body 返回。除了 yield,也可以直接返回一个可迭代对象。
在标准库 wsgiref 中已经包含了一个简单的 WSGI APP,可以在 wsgiref.simple_server 中找到,可以看到,这也是在做相同的事情:
def demo_app(environ,start_response): from io import StringIO stdout = StringIO() print("Hello world!", file=stdout) print(file=stdout) h = sorted(environ.items()) for k,v in h: print(k,'=',repr(v), file=stdout) start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')]) return [stdout.getvalue().encode("utf-8")]
将这个 App 运行起来如下:
在 Django 中,可以在默认 app 下的 wsgi.py 中找到 get_wsgi_application
,Django 通过这个方法创建并返回了一个 WSGIHandle
,其本质,依然是一个 WSGI APP,可以看其 __call__
方法:
class WSGIHandler(base.BaseHandler): request_class = WSGIRequest def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.load_middleware() def __call__(self, environ, start_response): set_script_prefix(get_script_name(environ)) signals.request_started.send(sender=self.__class__, environ=environ) request = self.request_class(environ) response = self.get_response(request) response._handler_class = self.__class__ status = '%d %s' % (response.status_code, response.reason_phrase) response_headers = list(response.items()) for c in response.cookies.values(): response_headers.append(('Set-Cookie', c.output(header=''))) start_response(status, response_headers) if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'): response = environ['wsgi.file_wrapper'](response.file_to_stream) return response
WSGI 服务器
从 WSGI APP 的写法上就基本能推测出 WSGI 服务器做了什么,因此可以尝试实现一个简陋的 WSGI 服务器:
def run_wsgi_app(app, environ): from io import StringIO body = StringIO() def start_response(status, headers): body.write('Status: {}\r\n'.format(status)) for header in headers: body.write("{}: {}".format(*header)) return body.write iterable = app(environ, start_response) try: if not body.getvalue(): raise RuntimeError("No exec start_response") body.write("\r\n{}\r\n".format('\r\n'.join(line for line in iterable))) finally: if hasattr(iterable, "close") and callable(iterable.close): iterable.close() # 这里瞎扯 return body.getvalue()
对于真正(可用)的 WSGI 服务器,常用的比如 Gunicorn,在不同的 Worker(gunicorn.worker 模块中)中,都实现了一个叫 handle_request
的类方法,这个方法便是调用 WSGI APP,并完成 Response 拼装的,虽然不同的 Worker 的实现略有差异,但比较共通的代码:
respiter = self.wsgi(environ, resp.start_response) try: if isinstance(respiter, environ['wsgi.file_wrapper']): resp.write_file(respiter) else: for item in respiter: resp.write(item) resp.close() request_time = datetime.now() - request_start self.log.access(resp, req, environ, request_time) finally: if hasattr(respiter, "close"): respiter.close()
这段代码便是调用 WSGI APP,并通过循环把 Body 写入到 resp 中。
中间件
因为 WSGI 的定义方式,可以写多个 WSGI APP 进行嵌套并处理不同的逻辑,比如:
def first_wsgi_app(environ, start_response): import logging logging.info("new request") rsp = second_wsgi_app(environ, start_response) logging.info("finish request") return rsp def second_wsgi_app(environ, start_response): if environ.get("HTTP_ROLE") == "ADMIN": return third_wsgi_app(environ, start_response) status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) yield "Hello User!" def third_wsgi_app(environ, start_response): status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) yield "Hello Admin!"
这时候我们把第一个 WSGI APP first_wsgi_app
传给 Server。在执行时, first_wsgi_app
可以完成日志的记录, second_wsgi_app
可以完成鉴权, third_wsgi_app
来真正的处理请求。
这种 App 的洋葱结构,被正伦亲切的称为俄罗斯套娃。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
第一本Docker书 修订版
詹姆斯·特恩布尔 (James Turnbull) / 李兆海、刘斌、巨震 / 人民邮电出版社 / 2016-4-1 / CNY 59.00
Docker是一个开源的应用容器引擎,开发者可以利用Docker打包自己的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。 本书由Docker公司前服务与支持副总裁James Turnbull编写,是Docker开发指南。本书专注于Docker 1.9及以上版本,指导读者完成Docker的安装、部署、管理和扩展,带领读者经历从测试到生产的整个开发生......一起来看看 《第一本Docker书 修订版》 这本书的介绍吧!