一。前言html
一个Web服务器网关接口 (WSGI)服务器实现了WSGI接口的Web服务器端运行的Python的Web应用程序。python
传统的Web服务器不理解或有任何方式来运行Python应用程序。在20世纪90年代后期,名为Grisha Trubetskoy的开发人员 提出了一个名为mod_python的Apache模块 来执行任意的Python代码。在20世纪90年代后期和21世纪初,Apache配置的mod_python运行了大多数Python Web应用程序。nginx
可是,mod_python不是标准规范。这只是一个容许Python代码在服务器上运行的实现。因为mod_python的开发陷入僵局,安全漏洞被发现,社区认识到,须要一致的方式来执行Web应用程序的Python代码。web
所以,Python社区提出了WSGI做为模块和容器能够实现的标准接口。WSGI如今是运行Python Web应用程序的被接受的方法。sql
WSGI标准v1.0在PEP 0333中指定 。截至2010年9月,WSGI v1.0被PEP 3333取代 ,它定义了v1.0.1 WSGI标准。若是您使用Python 2.x,而且您符合PEP 0333,那么您也符合3333.较新版本仅仅是Python 3的更新,而且有关于如何处理unicode的说明。apache
在的wsgiref的Python 2.x中和 的wsgiref在Python 3.x的 是内置到Python的标准库WSGI规范的参考实现,所以它能够被用来建造WSGI服务器和应用程序安全
PEP 0333 – Python Web Server Gateway Interface 是一种 web server or gateway 和 python web application or framework 之间简单通用的接口,符合这种接口的 application 可运行在全部符合该接口的 server 上。通俗的讲,WSGI 规范了一种简单的接口,解耦了 server 和 application,使得双边的开发者更加专一自身特性的开发。ruby
Application/framework 端必须定义一个 callable object,callable object 能够是如下三者之一:服务器
Callable object 必须知足如下两个条件:网络
示例:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return ['HELLO WORLD!']
class ApplicationClass(object): def __init__(self, environ, start_response): self.environ = environ self.start_response = start_response def __iter__(self): self.start_response('200 OK', [('Content-type', 'text/plain')]) yield "Hello world!n"
environ
和 start_response
由 http server 提供并实现environ
变量是包含了环境信息的字典Application
内部在返回前调用 start_response
start_response
也是一个 callable,接受两个必须的参数,status
(HTTP状态)和 response_headers
(响应消息的头)服务器端主要专一 HTTP 层面的业务,重点是接收 HTTP 请求和提供并发。
每当收到 HTTP 请求,服务器接口端必须调用 callable object:
Middleware 处于 服务层和 应用接口层 之间,每一个 middleware 实现不一样的功能,咱们一般根据需求选择相应的 middleware 并组合起来,实现所需的功能。其做用有如下几点:
class IPBlacklistMiddleware(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): ip_addr = environ.get('HTTP_HOST').split(':')[0] if ip_addr not in ('127.0.0.1'): return forbidden(start_response) return self.app(environ, start_response) def forbidden(start_response): start_response('403 Forbidden', [('Content-Type', 'text/plain')]) return ['Forbidden'] def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return ['Hello World!'] if __name__ == '__main__': from wsgiref.simple_server import make_server application = IPBlacklistMiddleware(application) server = make_server('0.0.0.0', 8080, application) server.serve_forever()
大体了解WSGI框架后咱们来看下Paste+Pastedeploy+route+webob开发,openstack开发使用的就是此开发框架,主要使用到的一些模块是:
Eventlet 是一个基于协程的 Python 高并发网络库,额如下特色:
Paste Deployment是用于查找和配置WSGI应用程序和服务器的系统。对于WSGI应用程序消费者,它提供了从配置文件或Python Egg加载WSGI应用程序的单一简单函数(loadapp)。对于WSGI应用程序提供商,它只须要一个简单的入口点到您的应用程序,以便应用程序用户不须要暴露于应用程序的实现细节。
Paste.deploy 一般要求 application 实现一个 factory 的类方法,以下:
class TestApplication(object): def __init__(self): pass @webob.dec.wsgify def __call__(self, req): return self.router @classmethod def factory(cls, global_conf, **local_conf): return cls()
if '__main__' == __name__: application = loadapp('config:%s/config.ini' % (CONF)) server = eventlet.spawn(wsgi.server, eventlet.listen(('0.0.0.0', 8080)), application) server.wait()
配置文件说明,一个配置文件有不一样的分段,但pastedeploy 关心的是前缀,好比app:main or filter:errors,:分隔部分以后是是这个分段的name,前一部分是这个分段的type类型,前段给的类型,后段名字将被忽略。
一个简单的INI配置文件格式是 name = value.能够经过缩进后续行来扩展配置,#是对前面配置的评论标注。
一般,您有一个或两个部分,称为“main”:一个应用程序部分(app:main)和一个服务器部分(server:main)。(复合:…表示向多个应用程序发送的内容(如如下示例)。
[composite:main] use = egg:Paste#urlmap / = home /blog = blog /wiki = wiki /cms = config:cms.ini [app:home] use = egg:Paste#static document_root = %(here)s/htdocs [filter-app:blog] use = egg:Authentication#auth next = blogapp roles = admin htpasswd = /home/me/users.htpasswd [app:blogapp] use = egg:BlogApp database = sqlite:/home/me/blog.db [app:wiki] use = call:mywiki.main:application database = sqlite:/home/me/wiki.db
urlmap 至关于作了多路由的分发,请求http://xxxx/ 时会请求分段名称为home的app, 走到相对的处理函数或者controller,其余类同
第一分段解释:
[composite:main] use = egg:Paste#urlmap / = home /blog = blog /cms = config:cms.ini
composite 分段将请求分发给其余applications程序,use = egg:Paste#urlmap 意思是这个composite程序将使用paste名称为urlmap的包, urlmap是一个特别常见的复合应用程序,它使用路径前缀将请求映射到另外一个应用程序。其余的如/ /blog 是应用程序,最后一个/cms,代表指的是另外一个ini配置文件。
[app:home] use = egg:Paste#static document_root = %(here)s/htdocs
egg:Paste#static 静态文件配置,使用paste包static只提供静态文件,须要指定document_root目录
from paste.deploy import loadapp wsgi_app = loadapp('config:/path/to/config.ini')
section的type能够有:app、composite、filter、pipeline、filter-app等。学习具体更多配置详参官网
Routes 是基于 ruby on rails 的 routes system 开发的 python 库,它根据 http url 把请求映射到具体的方法,routes 简单易用,可方便的构建 Restful 风格的 url。
示例:
import eventlet from eventlet import wsgi from paste.deploy import loadapp import routes import routes.middleware as middleware import webob.dec import webob.exc from manager.settings import CONF class TestController(object): def index(self, req): return 'GET' def create(self, req): return 'POST' def delete(self, req): return 'DELETE' def update(self, req): return 'PUT' class Resource(object): def __init__(self, controller): self.controller = controller() @webob.dec.wsgify def __call__(self, req): match = req.environ['wsgiorg.routing_args'][1] action = match['action'] if hasattr(self.controller, action): method = getattr(self.controller, action) return method(req) return webob.exc.HTTPNotFound() class TestApplication(object): def __init__(self): self.mapper = routes.Mapper() self.mapper.resource('test', 'tests', controller=Resource(TestController)) self.router = middleware.RoutesMiddleware(self.dispatch, self.mapper) @webob.dec.wsgify def __call__(self, req): return self.router @classmethod def factory(cls, global_conf, **local_conf): return cls() @staticmethod @webob.dec.wsgify def dispatch(req): match = req.environ['wsgiorg.routing_args'][1] return match['controller'] if match else webob.exc.HTTPNotFound() if '__main__' == __name__: application = loadapp('config:%s/config.ini' % (CONF)) server = eventlet.spawn(wsgi.server, eventlet.listen(('0.0.0.0', 8080)), application) server.wait()