成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

通過demo學(xué)習(xí)OpenStack開發(fā)所需的基礎(chǔ)知識 -- API服務(wù)(2)

Martin91 / 862人閱讀

摘要:這種表示具體的。中其他的關(guān)鍵字則是函數(shù)的參數(shù),用于表示不同的前綴。這個是這個指定的第一個,作用是限制請求的大小。表示實現(xiàn)主要功能的應(yīng)用,是一個標準的。對象是根據(jù)中的配置來處理的。最后會把請求交給進行處理。

本文會重點講解OpenStack中使用的API開發(fā)框架的使用。但是本文的目的并不是覆蓋這些框架的使用細節(jié),而是通過說明重要的部分,降低初學(xué)者的入門的門檻??蚣艿氖褂眉毠?jié)都可以從文檔中找到。說明一下,除非特殊說明,本文中的相對路徑都是相對于項目源碼目錄的相對路徑。

Paste + PasteDeploy + Routes + WebOb

我們在API服務(wù)(1)中已經(jīng)提到了,這個框架只在早期開始的項目中使用,新的項目都已經(jīng)轉(zhuǎn)到Pecan框架了。但是,早期的項目都是比較核心的項目,因此我們還是要學(xué)會如何使用這個框架。我們會以Keystone項目為例,來說明如何閱讀使用這個框架的開發(fā)的API代碼。

重點在于確定URL路由

RESTful API程序的主要特點就是URL path會和功能對應(yīng)起來。這點從API文檔就可以看得出來,比如用戶管理的功能一般都放在/user這個路徑下。因此,看一個RESTful API程序,一般都是看它實現(xiàn)了哪些URL path,以及每個path對應(yīng)了什么功能,這個一般都是由框架的URL路由功能負責的。所以,熟悉一個RESTful API程序的重點在于確定URL路由。本章所說的這個框架對于初學(xué)者的難點也是如何確定URL路由。

WSGI入口和中間件

作為基礎(chǔ)知識,你需要先了解一下WSGI的相關(guān)概念,可以參考這篇文章WSGI簡介。

WSGI入口

API服務(wù)(1)中提到了WSGI可以使用Apache進行部署,也可以使用eventlet進行部署。Keystone項目同時提供了這兩種方案的代碼,也就是我們要找的WSGI的入口。

Keystone項目在httpd/目錄下,存放了可以用于Apache服務(wù)器部署WSGI服務(wù)的文件。其中,wsgi-keystone.conf是一個mod_wsgi的示例配置文件,keystone.py則是WSGI應(yīng)用程序的入口文件。httpd/keystone.py也就是我們要找的入口文件之一。這個文件的內(nèi)容很簡單:

import os

from keystone.server import wsgi as wsgi_server

name = os.path.basename(__file__)

application = wsgi_server.initialize_application(name)

文件中創(chuàng)建了WSGI入口需要使用的application對象。

keystone-all命令則是采用eventlet來進行部署時的入口,可以從setup.cfg文件按中確定keystone-all命令的入口:

[entry_points]
console_scripts =
    keystone-all = keystone.cmd.all:main
    keystone-manage = keystone.cmd.manage:main

setup.cfg文件的entry_points部分可以看出,keystone-all的入口是keystone/cmd/all.py文件中的main()函數(shù),這個函數(shù)的內(nèi)容也很簡單:

def main():
    eventlet_server.run(possible_topdir)

main()函數(shù)的主要作用就是啟動一個eventlet_server,配置文件從possible_topdir中查找。因為eventlet的部署方式涉及到eventlet庫的使用方法,本文不再展開說明。讀者可以在學(xué)會確定URL路由后再回來看這個代碼。下面,繼續(xù)以httpd/keystone.py文件作為入口來說明如何閱讀代碼。

Paste和PasteDeploy

httpd/keystone.py中調(diào)用的initialize_application(name)函數(shù)載入了整個WSGI應(yīng)用,這里主要用到了Paste和PasteDeploy庫。

def initialize_application(name):
    ...
    def loadapp():
        return keystone_service.loadapp(
            "config:%s" % config.find_paste_config(), name)

    _unused, application = common.setup_backends(
        startup_application_fn=loadapp)
    return application

上面是刪掉無關(guān)代碼后的initialize_application()函數(shù)。config.find_paste_config()用來查找PasteDeploy需要用到的WSGI配置文件,這個文件在源碼中是etc/keystone-paste.ini文件,如果在線上環(huán)境中,一般是/etc/keystone-paste.init。keystone_service.loadapp()函數(shù)內(nèi)部則調(diào)用了paste.deploy.loadapp()函數(shù)來加載WSGI應(yīng)用,如何加載則使用了剛才提到的keystone-paste.ini文件,這個文件也是看懂整個程序的關(guān)鍵。

name很關(guān)鍵

在上面的代碼中我們可以看到,name這個變量從httpd/keystone.py文件傳遞到initialize_application()函數(shù),又被傳遞到keystone_service.loadapp()函數(shù),最終被傳遞到paste.deploy.loadapp()函數(shù)。那么,這個name變量到底起什么作用呢?先把這個問題放在一邊,我們后面再來解決它。

paste.ini

使用Paste和PasteDeploy模塊來實現(xiàn)WSGI服務(wù)時,都需要一個paste.ini文件。這個文件也是Paste框架的精髓,這里需要重點說明一下這個文件如何閱讀。

paste.ini文件的格式類似于INI格式,每個section的格式為[type:name]。這里重要的是理解幾種不同type的section的作用。

composite: 這種section用于將HTTP請求分發(fā)到指定的app。

app: 這種section表示具體的app。

filter: 實現(xiàn)一個過濾器中間件。

pipeline: 用來把把一系列的filter串起來。

上面這些section是在keystone的paste.ini中用到的,下面詳細介紹一下如何使用。這里需要用到WSGIMiddleware(WSGI中間件)的知識,可以在WSGI簡介這篇文章中找到。

section composite

這種section用來決定如何分發(fā)HTTP請求。Keystone的paste.ini文件中有兩個composite的section:

[composite:main]
use = egg:Paste#urlmap
/v2.0 = public_api
/v3 = api_v3
/ = public_version_api

[composite:admin]
use = egg:Paste#urlmap
/v2.0 = admin_api
/v3 = api_v3
/ = admin_version_api

在composite seciont中,use是一個關(guān)鍵字,指定處理請求的代碼。egg:Paste#urlmap表示到Paste模塊的egg-info中去查找urlmap關(guān)鍵字所對應(yīng)的函數(shù)。在virtualenv環(huán)境下,是文件/lib/python2.7/site-packages/Paste-2.0.2.dist-info/metadata.json

{
    ...
    "extensions": {
        ...
        "python.exports": {
            "paste.composite_factory": {
                "cascade": "paste.cascade:make_cascade",
                "urlmap": "paste.urlmap:urlmap_factory"
            },
    ...
}

在這個文件中,你可以找到urlmap對應(yīng)的是paste.urlmap:urlmap_factory,也就是paste/urlmap.py文件中的urlmap_factory()函數(shù)。

composite section中其他的關(guān)鍵字則是urlmap_factory()函數(shù)的參數(shù),用于表示不同的URL path前綴。urlmap_factory()函數(shù)會返回一個WSGI app,其功能是根據(jù)不同的URL path前綴,把請求路由給不同的app。以[composite:main]為例:

[composite:main]
use = egg:Paste#urlmap
/v2.0 = public_api       # /v2.0 開頭的請求會路由給public_api處理
/v3 = api_v3             # /v3 開頭的請求會路由個api_v3處理
/ = public_version_api   # / 開頭的請求會路由給public_version_api處理

路由的對象其實就是paste.ini中其他secion的名字,類型必須是app或者pipeline。

section pipeline

pipeline是把filter和app串起來的一種section。它只有一個關(guān)鍵字就是pipeline。我們以api_v3這個pipeline為例:

[pipeline:api_v3]
# The last item in this pipeline must be service_v3 or an equivalent
# application. It cannot be a filter.
pipeline = sizelimit url_normalize request_id build_auth_context token_auth admin_token_auth json_body ec2_extension_v3 s3_extension simple_cert_extension revoke_extension federation_extension oauth1_extension endpoint_filter_extension endpoint_policy_extension service_v3

pipeline關(guān)鍵字指定了很多個名字,這些名字也是paste.ini文件中其他section的名字。請求會從最前面的section開始處理,一直向后傳遞。pipeline指定的section有如下要求:

最后一個名字對應(yīng)的section一定要是一個app

非最后一個名字對應(yīng)的section一定要是一個filter

section filter

filter是用來過濾請求和響應(yīng)的,以WSGI中間件的方式實現(xiàn)。

[filter:sizelimit]
paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory

這個是api_v3這個pipeline指定的第一個filter,作用是限制請求的大小。其中的paste.filter_factory表示調(diào)用哪個函數(shù)來獲得這個filter中間件。

section app

app表示實現(xiàn)主要功能的應(yīng)用,是一個標準的WSGI application。

[app:service_v3]
paste.app_factory = keystone.service:v3_app_factory

paste.app_factory表示調(diào)用哪個函數(shù)來獲得這個app。

總結(jié)一下

paste.ini中這一大堆配置的作用就是把我們用Python寫的WSGI application和middleware串起來,規(guī)定好HTTP請求處理的路徑。

name是用來確定入口的

上面我們提到了一個問題,就是name變量的作用到底是什么?name變量表示paste.ini中一個section的名字,指定這個section作為HTTP請求處理的第一站。在Keystone的paste.ini中,請求必須先由[composite:main]或者[composite:admin]處理,所以在keystone項目中,name的值必須是main或者admin。

上面提到的httpd/keystone.py文件中,name等于文件名的basename,所以實際部署中,必須把keystone.py重命名為main.py或者admin.py

舉個例子

一般情況下,從Keystone服務(wù)獲取一個token時,會使用下面這個API:

POST http://hostname:35357/v3/auth/tokens

我們根據(jù)Keystone的paste.ini來說明這個API是如何被處理的:

hostname:35357 這一部分是由Web服務(wù)器處理的,比如Apache。然后,請求會被轉(zhuǎn)到WSGI的入口,也就是httpd/keystone.py中的application對象取處理。

application對象是根據(jù)paste.ini中的配置來處理的。這里會先由[composite:admin]來處理(一般是admin監(jiān)聽35357端口,main監(jiān)聽5000端口)。

[composite:admin]發(fā)現(xiàn)請求的path是/v3開頭的,于是就把請求轉(zhuǎn)發(fā)給[pipeline:api_v3]去處理,轉(zhuǎn)發(fā)之前,會把/v3這個部分去掉。

[pipeline:api_v3]收到請求,path是/auth/tokens,然后開始調(diào)用各個filter來處理請求。最后會把請求交給[app:service_v3]進行處理。

[app:service_v3]收到請求,path是/auth/tokens,然后交給最終的WSGI app去處理。

下一步

到此為止,paste.ini中的配置的所有工作都已經(jīng)做完了。下面請求就要轉(zhuǎn)移到最終的app內(nèi)部去處理了。前面已經(jīng)說過了,我們的重點是確定URL路由,那么現(xiàn)在還有一部分的path的路由還沒確定,/auth/tokens,這個還需要下一步的工作。

中間件的實現(xiàn)

上面我們提到paste.ini中用到了許多的WSGI中間件,那么這些中間件是如何實現(xiàn)的呢?我們來看一個例子就知道了。

[filter:build_auth_context]
paste.filter_factory = keystone.middleware:AuthContextMiddleware.factory

build_auth_context這個中間件的作用是在WSGI的environ中添加KEYSTONE_AUTH_CONTEXT這個鍵,包含的內(nèi)容是認證信息的上下文。實現(xiàn)這個中間件的類繼承關(guān)系如下:

keystone.middleware.core.AuthContextMiddleware
  -> keystone.common.wsgi.Middleware
    -> keystone.common.wsgi.Application
      -> keystone.common.wsgi.BaseApplication

這里實現(xiàn)的關(guān)鍵主要在前面兩個類中。

keystone.common.wsgi.Middleware類實現(xiàn)了__call__()方法,這個就是WSGI中application端被調(diào)用時運行的方法。

class Middleware(Application):
    ...
    @webob.dec.wsgify()
    def __call__(self, request):
        try:
            response = self.process_request(request)
            if response:
                return response
            response = request.get_response(self.application)
            return self.process_response(request, response)
        except exceptin.Error as e:
            ...
        ...

__call__()方法實現(xiàn)為接收一個request對象,返回一個response對象的形式,然后使用WebOB模塊的裝飾器webob.dec.wsgify()將它變成標準的WSGI application接口。這里的request和response對象分別是 webob.Requestwebob.Response。這里,__call__()方法內(nèi)部調(diào)用了self.process_request()方法,這個方法在keystone.middleware.core.AuthContextMiddleware中實現(xiàn):

class AuthContextMiddleware(wsgi.Middleware):
    ...
    def process_request(self, request):
        ...
        request.environ[authorization.AUTH_CONTEXT_ENV] = auth_context

這個函數(shù)會根據(jù)功能設(shè)計創(chuàng)建auth_context,然后賦值給request.environ["KEYSTONE_AUTH_CONTEXT]`,這樣就能通過WSGI application方法的environ傳遞到下一個WSGI application中去了。

最后的Application

上面我們已經(jīng)看到了,對于/v3開頭的請求,在paste.ini中會被路由到[app:service_v3]這個section,會交給keystone.service:v3_app_factory這個函數(shù)生成的application處理。最后這個application需要根據(jù)URL path中剩下的部分,/auth/tokens,來實現(xiàn)URL路由。從這里開始,就需要用到Routes模塊了。

同樣由于篇幅限制,我們只能展示Routes模塊的大概用法。Routes模塊是用Python實現(xiàn)的類似Rails的URL路由系統(tǒng),它的主要功能就是把path映射到對應(yīng)的動作。

Routes模塊的一般用法是創(chuàng)建一個Mapper對象,然后調(diào)用該對象的connect()方法把path和method映射到一個controller的某個action上,這里controller是一個自定義的類實例,action是表示controller對象的方法的字符串。一般調(diào)用的時候還會指定映射哪些方法,比如GET或者POST之類的。

舉個例子,來看下keystone/auth/routers.py的代碼:

class Routers(wsgi.RoutersBase):

    def append_v3_routers(self, mapper, routers):
        auth_controller = controllers.Auth()

        self._add_resource(
            mapper, auth_controller,
            path="/auth/tokens",
            get_action="validate_token",
            head_action="check_token",
            post_action="authenticate_for_token",
            delete_action="revoke_token",
            rel=json_home.build_v3_resource_relation("auth_tokens"))

    ...

這里調(diào)用了自己Keystone自己封裝的_add_resource()方法批量為一個/auth/tokens這個path添加多個方法的處理函數(shù)。其中,controller是一個controllers.Auth實例,也就是 keystone.auth.controllers.Auth。其他的參數(shù),我們從名稱可以猜出其作用是指定對應(yīng)方法的處理函數(shù),比如get_action用于指定GET方法的處理函數(shù)為validate_token。我們再深入一下,看下_add_resource()這個方法的實現(xiàn):

    def _add_resource(self, mapper, controller, path, rel,
                      get_action=None, head_action=None, get_head_action=None,
                      put_action=None, post_action=None, patch_action=None,
                      delete_action=None, get_post_action=None,
                      path_vars=None, status=json_home.Status.STABLE):
        ...
        if get_action:
            getattr(controller, get_action)  # ensure the attribute exists
            mapper.connect(path, controller=controller, action=get_action,
                           conditions=dict(method=["GET"]))
        ...

這個函數(shù)其實很簡單,就是調(diào)用mapper對象的connect方法指定一個path的某些method的處理函數(shù)。

Keystone項目的代碼結(jié)構(gòu)

Keystone項目把每個功能都分到多帶帶的目錄下,比如token相關(guān)的功能是放在keystone/token/目錄下,assignment相關(guān)的功能是放在keystone/assignment/目錄下。目錄下都一般會有三個文件:routers.py, controllers.py, core.py。routers.py中實現(xiàn)了URL路由,把URL和controllers.py中的action對應(yīng)起來;controllers.py中的action調(diào)用core.py中的底層接口實現(xiàn)RESTful API承諾的功能。所以,我們要進一步確定URL路由是如何做的,就要看routers.py文件。

注意,這個只是Keystone項目的結(jié)構(gòu),其他項目即使用了同樣的框架,也不一定是這么做的。

Keystone中的路由匯總

每個模塊都定義了自己的路由,但是這些路由最終要還是要通過一個WSGI application來調(diào)用的。上面已經(jīng)提到了,在Keystone中,/v3開頭的請求最終都會交給keystone.service.v3_app_factory這個函數(shù)生成的application來處理。這個函數(shù)里也包含了路由最后分發(fā)的秘密,我們來看代碼:

def v3_app_factory(global_conf, **local_conf):
    ...
    mapper = routes.Mapper()
    ...
    
    router_modules = [auth,
                      assignment,
                      catalog,
                      credential,
                      identity,
                      policy,
                      resource]
    ...

    for module in router_modules:
        routers_instance = module.routers.Routers()
        _routers.append(routers_instance)
        routers_instance.append_v3_routers(mapper, sub_routers)

    # Add in the v3 version api
    sub_routers.append(routers.VersionV3("public", _routers))
    return wsgi.ComposingRouter(mapper, sub_routers)

v3_app_factory()函數(shù)中先遍歷了所有的模塊,將每個模塊的路由都添加到同一個mapper對象中,然后把mapper對象作為參數(shù)用于初始化wsgi.ComposingRouter對象,所以我們可以判斷,這個wsgi.ComposingRouter對象一定是一個WSGI application,我們看看代碼就知道了:

class Router(object):
    """WSGI middleware that maps incoming requests to WSGI apps."""

    def __init__(self, mapper):
        self.map = mapper
        self._router = routes.middleware.RoutesMiddleware(self._dispatch,
                                                          self.map)

    @webob.dec.wsgify()
    def __call__(self, req):
        return self._router

    ...
    
class ComposingRouter(Router):
    def __init__(self, mapper=None, routers=None):
        ...

上述代碼證實了我們的猜測。這個ComposingRouter對象被調(diào)用時(在其父類Router中實現(xiàn)),會返回一個WSGI application。這個application中則使用了routes模塊的中間件來實現(xiàn)了請求路由,在routes.middleware.RoutesMiddleware中實現(xiàn)。這里對path進行路由的結(jié)果就是返回各個模塊的controllers.py中定義的controller。各個模塊的controller都是一個WSGI application,這個你可以通過這些controller的類繼承關(guān)系看出來。

但是這里只講到了,routes模塊把path映射到了一個controller,但是如何把對path的處理映射到controller的方法呢?這個可以從controller的父類keystone.common.wsgi.Application的實現(xiàn)看出來。這個Application類中使用了environ["wsgiorg.routing_args"]中的數(shù)據(jù)來確定調(diào)用controller的哪個方法,這些數(shù)據(jù)是由上面提到的routes.middleware.RoutesMiddleware設(shè)置的。

總結(jié)

到這里我們大概把Paste + PasteDeploy + Routes + WebOb這個框架的流程講了一遍,從本文的長度你就可以看出這個框架有多啰嗦,用起來有多麻煩。下一篇文章我們會講Pecan框架,我們的demo也將會使用Pecan框架來開發(fā)。

參考資源

本文主要提到了Python Paste中的各種庫,這些庫的相關(guān)文檔都可以在項目官網(wǎng)找到:http://pythonpaste.org/。

另外,routes庫的項目官網(wǎng)是:https://routes.readthedocs.org/en/latest/

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/37597.html

相關(guān)文章

  • 通過demo學(xué)習(xí)OpenStack開發(fā)需的基礎(chǔ)知識 -- API服務(wù)(1)

    摘要:通過,也就是通過各個項目提供的來使用各個服務(wù)的功能。通過使用的方式是由各個服務(wù)自己實現(xiàn)的,比如負責計算的項目實現(xiàn)了計算相關(guān)的,負責認證的項目實現(xiàn)了認證和授權(quán)相關(guān)的。的服務(wù)都是使用的方式來部署的。 使用OpenStack服務(wù)的方式 OpenStack項目作為一個IaaS平臺,提供了三種使用方式: 通過Web界面,也就是通過Dashboard(面板)來使用平臺上的功能。 通過命令行,也就...

    Jason_Geng 評論0 收藏0
  • 通過demo學(xué)習(xí)OpenStack開發(fā)需的基礎(chǔ)知識 -- 數(shù)據(jù)庫(2)

    摘要:在實際項目中,這么做肯定是不行的實際項目中不會使用內(nèi)存數(shù)據(jù)庫,這種數(shù)據(jù)庫一般只是在單元測試中使用。接下來,我們將會了解中單元測試的相關(guān)知識。 在上一篇文章,我們介紹了SQLAlchemy的基本概念,也介紹了基本的使用流程。本文我們結(jié)合webdemo這個項目來介紹如何在項目中使用SQLAlchemy。另外,我們還會介紹數(shù)據(jù)庫版本管理的概念和實踐,這也是OpenStack每個項目都需要做的...

    mingzhong 評論0 收藏0
  • 通過demo學(xué)習(xí)OpenStack開發(fā)需的基礎(chǔ)知識 -- API服務(wù)(4)

    摘要:到這里,我們的服務(wù)的框架已經(jīng)搭建完成,并且測試服務(wù)器也跑起來了。上面的代碼也就可以修改為再次運行我們的測試服務(wù)器,就可以返現(xiàn)返回值為格式了。我們先來完成利用來檢查返回值的代碼方法的第一個參數(shù)表示返回值的類型這樣就完成了的返回值檢查了。 上一篇文章說到,我們將以實例的形式來繼續(xù)講述這個API服務(wù)的開發(fā)知識,這里會使用Pecan和WSME兩個庫。 設(shè)計REST API 要開發(fā)REST AP...

    meislzhua 評論0 收藏0
  • 通過demo學(xué)習(xí)OpenStack開發(fā)需的基礎(chǔ)知識 -- 單元測試

    摘要:本文將進入單元測試的部分,這也是基礎(chǔ)知識中最后一個大塊。本文將重點講述和中的單元測試的生態(tài)環(huán)境。另外,在中指定要運行的單元測試用例的完整語法是。中使用模塊管理單元測試用例。每個項目的單元測試代碼結(jié)構(gòu)可 本文將進入單元測試的部分,這也是基礎(chǔ)知識中最后一個大塊。本文將重點講述Python和OpenStack中的單元測試的生態(tài)環(huán)境。 單元測試的重要性 github上有個人畫了一些不同語言的學(xué)...

    douzifly 評論0 收藏0
  • 通過demo學(xué)習(xí)OpenStack開發(fā)需的基礎(chǔ)知識 -- API服務(wù)(3)

    摘要:從上面的例子可以看出,決定響應(yīng)類型的主要是傳遞給函數(shù)的參數(shù),我們看下函數(shù)的完整聲明參數(shù)用來指定返回值的模板,如果是就會返回內(nèi)容,這里可以指定一個文件,或者指定一個模板。用來做什么上面兩節(jié)已經(jīng)說明了可以比較好的處理請求中的參數(shù)以及控制返回值。 上一篇文章我們了解了一個巨啰嗦的框架:Paste + PasteDeploy + Routes + WebOb。后來OpenStack社區(qū)的人受不...

    ybak 評論0 收藏0
  • 通過demo學(xué)習(xí)OpenStack開發(fā)需的基礎(chǔ)知識 -- 軟件包管理

    摘要:不幸的是,在軟件包管理十分混亂,至少歷史上十分混亂。的最大改進是將函數(shù)的參數(shù)單獨放到一個的文件中這些成為包的元數(shù)據(jù)?;诘陌姹咎柟芾怼5陌姹就茖?dǎo)這里重點說明一下基于的版本號管理這個功能。開發(fā)版本號的形式如下。 為什么寫這個系列 OpenStack是目前我所知的最大最復(fù)雜的基于Python項目。整個OpenStack項目包含了數(shù)十個主要的子項目,每個子項目所用到的庫也不盡相同。因此,對于...

    blastz 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<