Files
kernel_Notes/Zim/Programme/python/WSGI/wsgi初探.txt
2012-08-08 15:17:56 +08:00

196 lines
9.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2011-10-13T22:34:33+08:00
====== wsgi初探 ======
Created Thursday 13 October 2011
http://linluxiang.iteye.com/blog/799163
===== 前言 =====
本文不涉及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架构图吧。
{{~/sync/notes/zim/python/WSGI/wsgi初探/1.jpg}}
可以看出,服务器,容器和应用之间存在着十分纠结的关系。下面就要把这些纠结的关系理清楚。
===== WSGI应用 =====
WSGI应用其实就是一个**callable的对象**。举一个最简单的例子,假设存在如下的一个应用:
def application(environ, start_response):
status = '200 OK'
output = 'World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(12)]
__ write =__ start_response(status, response_headers)
write('Hello ')
return [output]
这个WSGI应用简单的可以用简陋来形容但是他的确是一个功能完整的WSGI应用。只不过给人留下了太多的疑点environ是什么start_response是什么为什么可以同时用write和return来返回内容
对于这些疑问不妨自己猜测一下他的作用。联想到CGI那么environ可能就是一系列的环境变量用来**表示HTTP请求的信息(客户端发过来的)**比如说method之类的。start_response可能是接受**HTTP response头信息(应用返回给客户端的Http头信息)**然后返回一个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的技术__才能实现比如说CGIFastCGI或者是embed的模式。
下面利用CGI的方式编写一个最简单的WSGI容器。关于WSGI容器的协议官方文档并没有具体的说如何实现只是介绍了一些需要约束的东西。具体内容看PEP3333中的协议。
#!/usr/bin/python
#encoding:utf8
import cgi
import cgitb
import sys
import os
#Make the environ argument
environ = {}
environ['REQUEST_METHOD'] = os.environ['REQUEST_METHOD']
environ['SCRIPT_NAME'] = os.environ['SCRIPT_NAME']
environ['PATH_INFO'] = os.environ['PATH_INFO']
environ['QUERY_STRING'] = os.environ['QUERY_STRING']
environ['CONTENT_TYPE'] = os.environ['CONTENT_TYPE']
environ['CONTENT_LENGTH'] = os.environ['CONTENT_LENGTH']
environ['SERVER_NAME'] = os.environ['SERVER_NAME']
environ['SERVER_PORT'] = os.environ['SERVER_PORT']
environ['SERVER_PROTOCOL'] = os.environ['SERVER_PROTOCOL']
environ['wsgi.version'] = (1, 0)
environ['wsgi.url_scheme'] = 'http'
environ['wsgi.input'] = sys.stdin
environ['wsgi.errors'] = sys.stderr
environ['wsgi.multithread'] = False
environ['wsgi.multiprocess'] = True
environ['wsgi.run_once'] = True
#make the start_response argument
#注意WSGI协议规定如果没有body内容是不能返回http response头信息的。
sent_header = False
res_status = None
res_headers = None
def write(body):
global sent_header
if sent_header:
sys.stdout.write(body)
else:
print res_status
for k, v in res_headers:
print k + ': ' + v
print
sys.stdout.write(body)
sent_header = True
def start_response(status, response_headers):
global res_status
global res_headers
res_status = status
res_headers = response_headers
return write
#here is the application
def application(environ, start_response):
status = '200 OK'
output = 'World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(12)]
write = start_response(status, response_headers)
write('Hello ')
return [output]
#here run the application
result = application(environ, start_response)
for value in result:
write(value)
看吧。其实实现一个WSGI容器也不难。
不过我从WSGI容器的设计中可以看出WSGI的应用设计上面存在着一个重大的问题就是为什么要提供两种方式返回数据明明只有一个write函数却既可以在application里面调用又可以在容器中传输应用的返回值来调用。如果说让我来设计的话直接把start_response给去掉了。就用application(environ)这个接口。传一个方法然后返回值就__是status, response_headers和一个字符串的列表__。实际传输的方法全部隐藏了。用户只需要从environ中读取数据处理就行了。。
可喜的是搜了一下貌似web3的标准里面应用的设计和我的想法类似。希望web3协议能早日普及。
====== Middleware中间件 ======
中间件是一类特殊的程序可以在容器和应用之间干一些坏事。。其实熟悉python的decorator的人就会发现这和decoraotr没什么区别。
下面来实现一个route的简单middleware。
class Router(object):
def __init__(self):
self.path_info = {}
def route(self, environ, start_response):
application = self.path_info[environ['PATH_INFO']]
return application(environ, start_response)
def __call__(self, path):
def wrapper(application):
self.path_info[path] = application
return wrapper
这就是一个很简单的路由功能的middleware。将上面那段wsgi容器的代码里面的应用修改成如下
router = Router()
#here is the application
@router('/hello')
def hello(environ, start_response):
status = '200 OK'
output = 'Hello'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
write = start_response(status, response_headers)
return [output]
@router('/world')
def world(environ, start_response):
status = '200 OK'
output = 'World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
write = start_response(status, response_headers)
return [output]
#here run the application
result = router.route(environ, start_response)
for value in result:
write(value)
这样,**容器就会自动的根据访问的地址找到对应的app执行了**。
====== 延伸 ======
写着写着怎么越来越像一个框架了看来Python开发框架真是简单。。
其实从另外一个角度去考虑。如果把application当作是一个运算单元。利用middleware调控IO和运算资源那么利用WSGI组成一个分布式的系统。
好吧,全文完。