Python WSGI

参考

因为服务都以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实例

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模块的简单使用

下面开始介绍PasteDeploy,它可以通过定义一个配置文件(ini格式,非常简单的键值赋值)把各个类似default_wsgi_app的WSGI响应处理函数组织起来(给他们不同的url路径映射),或者在其外围包裹一层filter对请求或者响应的内容做些预/后处理,这跟python的函数装饰器很像。但是我们不能掉进java的坑里面,什么AOP啊一大堆,简单实用又不失结构性的风格是派森大法最有魅力的地方。由于paste的配置文件非常灵活,还是从最简单的开始说起。

paste.ini

我们创建一个叫做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

先解释一下,

  • 在[DEFAULT]节中的变量定义都作为全局变量(如server_name)
  • [app:main]表示一个名称为main的WSGI实例的配置,在这里定义的变量作为专门用于这个实例的局部变量(如app_developer, app_version)
  • paste.app_factory = sample:custom_app_factory,表示WSGI的处理函数(如上例中的default_wsgi_app)由谁来生成,这里我们定义由sample模块中的custom_app_factory函数来生成

 paste.app_factory

其中paste.app_factory是Paste模块的一个参数它指向一个用于创建WSGI处理函数的函数,这个factory函数应该是如下形式:

paste.app_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这些。

 

posted @ 2014-08-12 21:58  卖程序的小歪  阅读(546)  评论(0编辑  收藏  举报