因为服务都以restful api的方式提供给外界访问,于是又要看WSGI,要用到PasteDeploy库,但是感觉资料有些难懂尤其是paste.ini的使用上,下面几篇还算不错
[1] http://wanglianghuaihua.blog.163.com/blog/static/5425153120138273471531/
[2] http://pythonpaste.org/deploy/ ([1]是对[2]的翻译,不过有些部分没有在[1]中出现,看看原文还是有必要的)
[3] http://smartzxy.iteye.com/blog/734050 (给了一个实例和一些分析)
WSGI的概念不想多扯,在我看来和CGI差不多,和ASP,PHP,serverlet总是那么相似,下面来看一个实例,超简单的一个http动态服务程序(端口8082)
#! /usr/bin/env python from wsgiref.simple_server import make_server import datetime def default_wsgi_app(environ, start_response): print 'environment varibles:' for (k, v) in environ.items(): print "%15s : %s" % (k, v) status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) return ['HaHa HeHe...generated@',str(datetime.datetime.now())] httpd = make_server('', 8082, default_wsgi_app) httpd.serve_forever()
其中的default_wsgi_app就是一个请求处理过程,把这个函数给make_server创建一个wsgi server(这里是httpd变量),它就会把监听到的请求扔给default_wsgi_app这个函数处理。函数的签名就是WSGI的标准,environ用于表示环境(含有GET/POST参数)变量是一个dict, start_response的使用如上述代码所示,用来输出响应头(HTTP Response Header)(当然可能更复杂,还没看过源码),响应头输出后,就要开始输出响应体(HTTP Response Body),即响应内容。default_wsgi_app函数通过返回一个字符串list来作为请求的响应内容。然后wsgi server接收到default_wsgi_app函数返回内容后,就开始将数据发送到请求端,这样一次请求-响应过程结束了。
如果我们把这个default_wsgi_app改的复杂一些,加入session保持(通过在http header中设置cookie和服务端的全局变量中创建一个映射),url dispatcher就跟其他的如serverlet非常像了。
下面开始介绍PasteDeploy,它可以通过定义一个配置文件(ini格式,非常简单的键值赋值)把各个类似default_wsgi_app的WSGI响应处理函数组织起来(给他们不同的url路径映射),或者在其外围包裹一层filter对请求或者响应的内容做些预/后处理,这跟python的函数装饰器很像。但是我们不能掉进java的坑里面,什么AOP啊一大堆,简单实用又不失结构性的风格是派森大法最有魅力的地方。由于paste的配置文件非常灵活,还是从最简单的开始说起。
我们创建一个叫做demo-paste.ini的文件,内容如下:
[DEFAULT] server_name = WSGI DEMO SERVER [app:main] app_developer = hgfeaon app_version = 1.0 paste.app_factory = sample:custom_app_factory
先解释一下,
其中paste.app_factory是Paste模块的一个参数它指向一个用于创建WSGI处理函数的函数,这个factory函数应该是如下形式:
The application is the most common. You define one like:
def app_factory(global_config, **local_conf): return wsgi_app
The global_config is a dictionary, and local configuration is passed as keyword arguments. The function returns a WSGI application.
这个函数有两个入参一个是global_config,是一个dict,内容就是在paste.ini文件中[DEFAULT]一节中定义的变量,而另外一个kwargs型参数local_config也可以看成是一个dict,存储的是每个节中的局部变量。根据这些变量可以定义一个工厂(之所以这样叫是因为用到了工厂模式)函数,来返回不同的WSGI响应函数,或者说对响应函数对象做一些配置。下面给出这个工厂函数(这个文件命名为sample.py和demo-paste.ini放在一个目录下):
#! /usr/bin/env python import datetime def default_wsgi_app(environ, start_response): print 'environment varibles:' for (k, v) in environ.items(): print "%15s : %s" % (k, v) status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) return ['HaHa HeHe...generated@',str(datetime.datetime.now())] def custom_app_factory(global_config, **local_config): # print the global variables defined in section [DEFAULT] section print 'Global Config:' for (k, v) in global_config.items(): print "%15s : %s" % (k, v) # print the local variables defined in this non-DEFAULT section # where the custom_app_factory is refered as # paste.app_factory = sample:custom_app_factory print 'Local Config:' for (k, v) in local_config.items(): print "%15s : %s" % (k, v) return default_wsgi_app
这里没有真正的使用这些参数,只是把它们打印了一下,然后返回用来处理请求的WSGI处理函数对象(default_wsgi_app)。下面可以通过在python解析器(从两个文件所在的目录启动)中执行以下代码来通过paste模块加载app(WSGI处理程序),注意这里配置文件的位置最好使用绝对路径。
>>> from wsgiref.simple_server import make_server >>> from paste.deploy import loadapp >>> wsgi_app = loadapp('config:e:/desktop/cloud_computing/wsgi/demo-paste.ini') Global Config: __file__ : e:\desktop\cloud_computing\wsgi\demo-paste.ini here : e:\desktop\cloud_computing\wsgi server_name : WSGI DEMO SERVER Local Config: app_version : 1.0 app_developer : hgfeaon >>> httpd = make_server('', 8082, wsgi_app) >>> httpd.serve_forever() 127.0.0.1 - - [12/Aug/2014 21:39:41] "GET / HTTP/1.1" 200 48 ...
这回make_server使用的WSGI处理函数(wsgi_app变量)就是paste模块通过loadapp函数读取配置文件载入进来的。
实际上wsgi_app并不一定要是一个函数对象,它可以是一个实现了__call__方法的类实例,反正它是callable的就可以了,一般就把他们叫做application,简称app,所以一路上看到的都是app:main,loadapp这些。