原文转自:http://blog.linluxiang.info/2011/03/03/wsgi-learn/
前言
本文不涉及WSGI的具体协议的介绍,也不会有协议完整的实现,甚至描述中还会掺杂着本人自己对于WSGI的见解。所有的WSGI官方定义请看http://www.python.org/dev/peps/pep-3333/。
WSGI是什么?
WSGI的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。
也就是说,WSGI就像是一座桥梁,一边连着web服务器,另一边连着用户的应用。但是呢,这个桥的功能很弱,有时候还需要别的桥来帮忙才能进行处理。
下面对本文出现的一些名词做定义。wsgi app,又称应用 ,就是一个WSGI application。wsgi container ,又称容器 ,虽然这个部分常常被称为handler,不过我个人认为handler容易和app混淆,所以我称之为容器。 wsgi_middleware ,又称*中间件*。一种特殊类型的程序,专门负责在容器和应用之间干坏事的。
一图胜千言,直接来一个我自己理解的WSGI架构图吧
可以看出,服务器,容器和应用之间存在着十分纠结的关系。下面就要把这些纠结的关系理清楚。
WSGI应用
WSGI应用其实就是一个callable的对象。举一个最简单的例子,假设存在如下的一个应用:
1 |
def application(environ, start_response):
|
4 |
response_headers = [( 'Content-type' , 'text/plain' ),
|
5 |
( 'Content-Length' , str ( 12 )]
|
6 |
write = start_response(status, response_headers)
|
这个WSGI应用简单的可以用简陋来形容,但是他的确是一个功能完整的WSGI应用。只不过给人留下了太多的疑点,environ是什么?start_response是什么?为什么可以同时用write和return来返回内容?
对于这些疑问,不妨自己猜测一下他的作用。联想到CGI,那么environ可能就是一系列的环境变量,用来表示HTTP请求的信息,比如说method 之类的。start_response,可能是接受HTTP response头信息,然后返回一个write函数,这个write函数可以把HTTP response的body返回给客户端。return自然是将HTTP response的body信息返回。不过这里的write和函数返回有什么区别?会不会是其实外围默认调用write对应用返回值进行处理?而且为什么 应用的返回值是一个列表呢?说明肯定存在一个对应用执行结果的迭代输出过程。难道说他隐含的支持iterator或者generator吗?
等等,应用执行结果?一个应用既然是一个函数,说明肯定有一个对象去执行它,并且可以猜到,这个对象把environ和start_response传给应用,将应用的返回结果输出给客户端。那么这个对象是什么呢?自然就是WSGI容器了。
WSGI容器
先说说WSGI容器的来源,其实这是我自己编造出来的一个概念。来源就是JavaServlet容器。我个人理解两者有相似的地方,就顺手拿过来用了。
WSGI容器的作用,就是构建一个让WSGI应用成功执行的环境。成功执行,意味着需要传入正确的参数,以及正确处理返回的结果,还得把结果返回给客户端。
所以,WSGI容器的工作流程大致就是,用webserver规定的通信方式,能从webserver获得正确的request信息,封装好,传给WSGI应用执行,正确的返回response。
一般来说,WSGI容器必须依附于现有的webserver的技术才能实现,比如说CGI,FastCGI,或者是embed的模式。
下面利用CGI的方式编写一个最简单的WSGI容器。关于WSGI容器的协议官方文档并没有具体的说如何实现,只是介绍了一些需要约束的东西。具体内容看PEP3333中的协议。
11 |
environ[ 'REQUEST_METHOD' ] = os.environ[ 'REQUEST_METHOD' ]
|
12 |
environ[ 'SCRIPT_NAME' ] = os.environ[ 'SCRIPT_NAME' ]
|
13 |
environ[ 'PATH_INFO' ] = os.environ[ 'PATH_INFO' ]
|
14 |
environ[ 'QUERY_STRING' ] = os.environ[ 'QUERY_STRING' ]
|
15 |
environ[ 'CONTENT_TYPE' ] = os.environ[ 'CONTENT_TYPE' ]
|
16 |
environ[ 'CONTENT_LENGTH' ] = os.environ[ 'CONTENT_LENGTH' ]
|
17 |
environ[ 'SERVER_NAME' ] = os.environ[ 'SERVER_NAME' ]
|
18 |
environ[ 'SERVER_PORT' ] = os.environ[ 'SERVER_PORT' ]
|
19 |
environ[ 'SERVER_PROTOCOL' ] = os.environ[ 'SERVER_PROTOCOL' ]
|
20 |
environ[ 'wsgi.version' ] = ( 1 , 0 )
|
21 |
environ[ 'wsgi.url_scheme' ] = 'http' |
22 |
environ[ 'wsgi.input' ] = sys.stdin
|
23 |
environ[ 'wsgi.errors' ] = sys.stderr
|
24 |
environ[ 'wsgi.multithread' ] = False |
25 |
environ[ 'wsgi.multiprocess' ] = True |
26 |
environ[ 'wsgi.run_once' ] = True |
38 |
sys.stdout.write(body)
|
41 |
for k, v in res_headers:
|
44 |
sys.stdout.write(body)
|
47 |
def start_response(status, response_headers):
|
51 |
res_headers = response_headers
|
55 |
def application(environ, start_response):
|
58 |
response_headers = [( 'Content-type' , 'text/plain' ),
|
59 |
( 'Content-Length' , str ( 12 )]
|
60 |
write = start_response(status, response_headers)
|
65 |
result = application(environ, start_response)
|
看吧。其实实现一个WSGI容器也不难。
不过我从WSGI容器的设计中可以看出WSGI的应用设计上面存在着一个重大的问题就是:为什么要提供两种方式返回数据?明明只有一个write函数,却 既可以在application里面调用,又可以在容器中传输应用的返回值来调用。如果说让我来设计的话,直接把start_response给去掉了。 就用application(environ)这个接口。传一个方法,然后返回值就是status, response_headers和一个字符串的列表。实际传输的方法全部隐藏了。用户只需要从environ中读取数据处理就行了。。
可喜的是,搜了一下貌似web3的标准里面应用的设计和我的想法类似。希望web3协议能早日普及。
Middleware中间件
中间件是一类特殊的程序,可以在容器和应用之间干一些坏事。。其实熟悉python的decorator的人就会发现,这和decoraotr没什么区别。
下面来实现一个route的简单middleware。
04 |
def route( self , environ, start_response):
|
05 |
application = self .path_info[environ[ 'PATH_INFO' ]]
|
06 |
return application(environ, start_response)
|
07 |
def __call__( self , path):
|
08 |
def wrapper(application):
|
09 |
self .path_info[path] = application
|
这就是一个很简单的路由功能的middleware。将上面那段wsgi容器的代码里面的应用修改成如下:
05 |
def hello(environ, start_response):
|
08 |
response_headers = [( 'Content-type' , 'text/plain' ),
|
09 |
( 'Content-Length' , str ( len (output)))]
|
10 |
write = start_response(status, response_headers)
|
14 |
def world(environ, start_response):
|
17 |
response_headers = [( 'Content-type' , 'text/plain' ),
|
18 |
( 'Content-Length' , str ( len (output)))]
|
19 |
write = start_response(status, response_headers)
|
22 |
result = router.route(environ, start_response)
|
这样,容器就会自动的根据访问的地址找到对应的app执行了。
延伸
写着写着,怎么越来越像一个框架了?看来Python开发框架真是简单。。
其实从另外一个角度去考虑。如果把application当作是一个运算单元。利用middleware调控IO和运算资源,那么利用WSGI组成一个分布式的系统。
分享到:
相关推荐
**Django项目初探** Django是一个基于Python的高级Web框架,它强调代码复用、高效开发和可维护性。这个名为"djangoProject.zip"的压缩包文件很可能包含了一个完整的Django项目,让我们来深入了解一下Django框架的...
【标题】:Django网站开发初探 在“django_website”这个项目中,我们首次涉足了使用Django框架构建Web应用程序的世界。Django是Python编程语言中的一个强大且灵活的开源Web框架,它遵循“干(Don't Repeat ...
《Flask初探:构建微型博客系统》 在IT行业中,Web开发是一个充满活力和创新的领域,Python作为一门简洁易学的编程语言,被广泛应用于各种Web应用的开发。其中,Flask框架以其轻量级、灵活的特点,深受开发者喜爱。...
**Flask-Sample:Python Web开发的初探** 在Python的世界里,Flask是一个轻量级的Web服务器网关接口(WSGI)Web应用框架,由Armin Ronacher开发。"烧瓶样品"(Flask-Sample)项目,正如其名,是一个用于教学和实践...
**Django初探:搭建第一个项目** Django是基于Python的高级Web开发框架,它以其“快速、可扩展且优雅”的设计哲学而受到广大开发者喜爱。"django-firstlook"是针对初学者的一个教程,旨在帮助你快速入门Django,...
**Flask-Project: Python Web 开发初探** 在Python的世界里,Flask是一个轻量级的Web应用框架,以其简洁、灵活的特性受到了开发者们的广泛喜爱。"flask-project"是一个基于Flask的项目模板,它为我们提供了一个快速...
【Django框架初探】 Django,一个由Python编写的高级Web框架,旨在使开发者能够快速、高效地创建功能丰富的网站应用。"django_first_task"是针对初学者的一个项目,旨在帮助新接触Django的人理解其基本工作流程和...
《Flask框架初探:基于StepDone项目的学习实践》 Flask是一款轻量级的Web应用框架,由Armin Ronacher开发,适用于Python编程。它以其简洁、灵活的特性深受开发者喜爱,尤其适合小型和中型项目。在“StepDone”这个...
**Django项目初探** 在Python的世界里,Django是一个非常流行且强大的Web框架,它遵循MVC(Model-View-Controller)设计模式,并提供了一种高效、安全的方式来构建动态网站和应用程序。"Project-Django"是您创建的...
**Flask_Blog:Python Web开发的初探** 在Python的世界里,Flask是一个轻量级的Web应用框架,以其简洁、灵活的特性受到了开发者们的广泛喜爱。"flask_blog"项目则是基于Flask框架构建的一个博客系统,旨在帮助...