转自http://blog.csdn.net/sxmatch/article/details/9832421
稍作整理
1 初识WSGI:
Wsgi是什么?Python Web服务网关接口? 可以先看下它不是什么:What WSGI¹ is not: a server, a python module, aframework, an API or any kind of software.它仅仅是一个web server和application之间的一个接口定义(http://webpython.codepoint.net/wsgi_tutorial),除了在PEP 3333之中,哪也不存在。Wsgi个人理解就是通过wsgi server收到web请求(http),然后把这个请求转给用户自定义的application去处理,并将执行结果返回。
个人理解,wsgi就是http request/reponse 的adapter和forwarding
2 Openstack中的wsgi
Openstack中使用了evenlet库提供的wsgi实现,evenlet是python的一个网络编程库(http://eventlet.net/),提供了许多有用的实现,包括协程,wsgi等,是网络并发编程的利器
以cinder为例,在cinder的cinder/wsgi.py中就import evenlet.wsgi,并在其基础上进一步抽象和包装,形成了wsgi.py中server类。
3 一个简单的wsgi服务
如何启动一个wsgi的server,注册application,并能响应http请求?先来看一个很简单的wsgi应用:
__author__= 'sxmatch'
"""the most simplest server of wsgi """
importwebob
importeventlet
fromeventlet import wsgi
fromwebob import Request
defmyapp(env, start_response):
status = "200 OK"
response_headers = [('Content-Type','text/plain')]
start_response(status, response_headers)
return ['Hello, World! I am sxmatch\r\n']
wsgi.server(eventlet.listen(('192.168.82.191',8090)), myapp)
该程序可以直接在服务器上跑起来
可以看到wsgi已经在8090端口上建立了,用Rest client发个消息看下,可以看到已经收到了响应“Hello, World! I am sxmatch”
在以上程序中:方法def myapp(env, start_response)就是用户自己的应用,起入参是wsgi规定好的,env为字典,start_response是个回调函数对象。在application中调用了这个start_response(status,response_headers).
可以说这个简单的server就是wsgi服务的骨架,如果在server和app之间再加入一些管道,就成了middleware,这不就和openstack的消息流处理过程一致了吗?
4 使用协程启动的wsgi服务
上文介绍的wsgi服务是直接使用wsgi.server去启动,进一步我们可以利用evenlet中线程去启动一个wsgi服务,在openstack的wsgi.py的Server类中就是使用这种方式。对于线程可以把它大致可以理解成允许子程序可以多次暂停和恢复执行,是实现多任务的一种有效手段。推测openstack中每个application都是通过一个线程来启动的,对于线程还需进一步的学习。基于以上,我们可以将第一段程序进行改造:
__author__= 'sxmatch'
"""useeventlet to start wsgi server"""
importwebob
importeventlet
fromeventlet import wsgi
fromwebob import Request
def myapp(env, start_response):
status = "200 OK"
response_headers = [('Content-Type','text/plain')]
start_response(status, response_headers)
return ['Hello, World!\r\n']
def start():
print "start wsgi server"
wsgi.server(eventlet.listen(('192.168.82.191', 8090)), myapp)
wsgi_server= eventlet.spawn(start)
wsgi_server.wait()
其中start函数是要启动的wsgi服务,而evenlet.spawn(start)正是启动一个线程去调用start函数,其返回结果是一个线程对象,这里有个问题需要提一下,如果只执行wsgi_server =eventlet.spawn(start)这句其实并没有真正调用start()方法,只有最后调用该对象的wait()方法后,才能真正执行start函数。Openstack中server类中的wait方法其实就是调用了线程的wait方法。服务启动及响应截图如下:
5 将application封装为class进行调用
为了进一步接近openstack中用法,将上文中的application函数可以封装为class进行调用,代码如下:
__author__= 'sxmatch'
"""calla application class"""
importwebob
importeventlet
fromeventlet import wsgi
fromwebob import Request
class Application(object):
def __call__(self, env, start_response):
status = "200 OK"
response_headers = [('Content-Type','text/plain')]
start_response(status,response_headers)
return ['Hello, World! I amsxmatch!\r\n']
def start():
print "start wsgi server"
app=Application()
wsgi.server(eventlet.listen(('192.168.82.191', 8090)), app)
wsgi_server= eventlet.spawn(start)
wsgi_server.wait()
其中可以看到,wsgi.server中app已经不是方法了,而是类实例,当然这个类要是可调用的,即要实现__call__方法。
6 使用webob来包装wsgi请求和响应
先介绍下webob: WebOb是一个Python库,主要是用在WSGI中对请求环境变量request environment(也就是WSGI应用中的参数environ)进行包装(提供wrapper),并提供了一个对象来方便的处理返回response消息。WebOb提供的对象映射了大多数的HTTP方法,包括头解析,content协商等。这里的映射,就是说只需要对对象进行操作,就可以完成HHTP的方法,从而大大简化开发难度(http://blog.csdn.net/ztejiagn/article/details/8722838)。
因此,可以将代码进一步优化:
__author__= 'w00194081'
"""usewebob to warpper request"""
import webob
import eventlet
from eventlet import wsgi
from webob import Request
from webob import Response
class Application(object):
def __call__(self, env, start_response):
req = Request(env)
print req.method
print req.body
print req.headers
response = Response(body="helloworld! I am sxmatch" , content_type='text/plain')
return response(env, start_response)
def start():
print "start wsgi server"
app=Application()
wsgi.server(eventlet.listen(('192.168.82.191', 8090)), app)
wsgi_server= eventlet.spawn(start)
wsgi_server.wait()
这里我使用webob将wsgi server传入的env封装为Webob中的Request对象,并打印了request对象中的method,body,headers属性。最后用Webob中的Response对象来封装响应