一。前言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()