1. 程式人生 > >OpenStack之RESTful API呼叫(一)

OpenStack之RESTful API呼叫(一)

前面兩篇文章分析了OpenStack同一組件下不同模組之間的通訊方式——RPC。這篇文章我們來繼續看一下OpenStack中的另一種通訊方式——RESTful API。

在OpenStack中,RESTful API用於不同元件之間的通訊。先來簡單的瞭解一下REST吧。

REST構建於HTTP協議之上,遵循並擴充套件和規範了傳統HTTP協議中的標準方法,以下是RESTful API定義的標準方法:

GET:查詢資源

POST:增加資源

PUT:更新資源

HEAD:驗證,包括使用者身份的驗證和資源的驗證

DELETE:刪除資源

同時,REST要求URL的格式遵守統一的規範,所有資源都具有唯一的ID。具體到OpenStack中,每個資源都有一個UUID,作為全域性唯一的標識。

在OpenStack中,所有的Web服務都是通過WSGI部署的。所謂WSGI是Python Web Server Gateway Interface的縮寫,是Python應用程式或框架和Web伺服器之間的一種介面。簡而言之,在Python Web 開發中,服務端程式可以分為兩個部分,一是伺服器程式,二是應用程式。前者負責把客戶端請求接收,整理,後者負責具體的邏輯處理。WSGI 是伺服器程式與應用程式的一個約定,它規定了雙方各自需要實現什麼介面,提供什麼功能,以便二者能夠配合使用。

本著設計的“開閉原則”,OpenStack通過配置檔案來配置WSGI服務的應用程式,PasteDeploy就是專門定製WSGI服務的開發包。

來看一個簡單的利用PasteDeploy定製的WSGI服務。

1)api-paste.ini配置檔案

OpenStack中,大部分元件的WSGI服務的配置是儲存在api-paste.ini檔案中的。這裡也用這樣的方式,api-paste.ini配置檔案如下

[app:main]
paste.app_factory = wsgi_paste:app_factory
以上配置中,第01行定義了名為main的app。app(應用程式)是PasteDeploy定義的一類部件。除了app外,PasteDeploy還定義了filer(過濾器)、pipeline(管道)和composite(複合體)等部件。第02行指定了main應用程式對應的工廠方法,該工廠方法必須返回一個方法的例項,該方法便是處理HTTP請求的應用程式。這個應用程式定義在wsgi_paste包的app_factory方法。

2)wsgi_paste.py檔案

在api-paste.ini配置檔案中引用的wsgi_paste包就是自定義的wsgi_paste.py檔案。

#應用程式
@wsgify
def application(request):
    return Response("Hello!\n")
#應用程式工廠方法
def app_factory(global,**local_config):
    return application
#根據api-paste.ini檔案動態載入應用程式
wsgi_app = loadapp('config:' + 配置檔案路徑)
#啟動WSGI服務
httpserver.serve(wsgi_app,host='127.0.0.1',port=8080)
這個程式很簡單,在一個api-paste.ini檔案中,可以定義多個部件,預設情況下,loadapp方法會把名為main的部件作為應用程式的入口。

3)程式碼測試

在終端執行python wsgi_paste.py啟動WSGI服務,然後在另一終端執行curl 127.0.0.1:8080傳送HTTP請求。

顯然,如果把一個WSGI服務的所有功能都放在一個方法中,是不利於擴充套件的,所以PasteDeploy引入了filter(過濾器)的概念,過濾器的作用和用於java開發中的struts框架中的過濾器的作用類似,多個過濾器和應用可以組成一個pipeline(管道),這個概念和struts中的stack(棧)的概念類似。下面來看一個應用了過濾器的WSGI服務的例子。

1)api-paste.ini配置檔案

[pipeline:main]
pipeline = auth hello
[app:hello]
paste.app_factory = wsgi_paste:app_factory
[filter:auth]
paste.filter_factory = wsgi_middleware:filter_factory
管道main部件由過濾器auth和應用程式hello組成。應用程式hello和上面的應用程式一樣,過濾器auth的工廠方法在wsgi_middleware包中定義。這裡的wsgi_middleware包對應於自定義的wsgi_middleware.py。

2)wsgi_middleware.py

#過濾器方法
@wsgify.middleware
def filter(request,app):
    if request.headers.get('X-Auth-Token') != 'open-sesame'  #驗證HTTP頭
        return exc.HTTPForbidden()
    return app(request)  #驗證成功,執行下一個過濾器或應用程式
#過濾器工廠方法
def filter_factory(global,**local_config):
    return filter 
這裡的filter方法與之前介紹的application方法的不同點在於多了一個app引數。

通過上面的方式可以方便的新增和刪除功能模組了,但是還存在兩個問題:

1. 過濾器和應用程式是用方法實現的,對於實現比較複雜的功能是不合適的

2. 通過httpserver來啟動WSGI服務,使得一個程序對應一個WSGI服務。對於WSGI服務的啟動、關閉操作都需要直接對程序進行操作

所以為了彌補以上兩個不足,在OpenStack中利用類來實現過濾器和應用程式並利用eventlet來啟動WSGI服務。其中eventlet是python多執行緒操作的類庫。

我們來看一下如何利用類來實現過濾器和應用程式的例項。

還是先來看一下api-paste.ini配置檔案:

[pipeline:main]
pipeline = auth hello
[app:hello]
paste.app_factory = app:Hello.app_factory
[filter:auth]
paste.filter_factory = middleware:Auth.filter.factory

可以看到配置檔案盒上面的差別不大,主要區別在於以下的幾個類。

(1)Auth類

class Auth(object):
    def __init__(self, app):
        self.app = app
    #工廠方法
    @classmethod
    def filter_factory(cls, global_config, **local_config):
        def _factory(app):
            return cls(app)
        return _factory
    #實現業務邏輯
    @wsgify(RequestClass=webob.Request)
    def __call__(self, request):
        resp = self.process_request(req)
        if resp:
            return resp
        return req.get_response(self.app)
    def process_request(self, request):
        if req.headers.get('X-Auth-Token') != 'open-sesame':
            return exc.HTTPForbidden()

上述程式碼定義了一個工廠方法,該工廠返回一個Auth類的例項,該例項是一個callable物件。當過濾器接收到請求後,會呼叫類中的__call__方法,__call__方法內部的邏輯和上面分析的“利用方法實現過濾器”中定義的過濾器方法一樣。

(2)Hello類

class Hello(object):
    #工廠方法
    @classmethod
    def app_factory(cls,global_config,**local_config):
        def _factory(app):
            return cls(app)
        return _factory
    @wsgify(RequestClass=Request)
    def __call__(request):
        return Response('Hello')
(3)Loader類
class Loader(object):
    def load_app(self):
        ini_path = os.path.normpath(
        os.path.join(os.path.abspath(sys.argv[0]),
                     os.pardir,
                     'api-paste.ini'))
        if not os.path.isfile(ini_path):
            print("Cannot find api-paste.ini.\n")
            exit(1)
        return deploy.loadapp('config:' + ini_path)

這個類比較簡單,它實現了一個load_app方法。該方法引用api-paste.ini配置檔案,並通過呼叫PasteDeploy的loadapp方法來載入應用程式。

(4)Server類

class Server(object):
    def __init__(self, app, host='0.0.0.0', port=0):
        self._pool = eventlet.GreenPool(10)
        self.app = app
        self._socket = eventlet.listen((host, port), backlog=10)
        (self.host, self.port) = self._socket.getsockname()
        print("Listening on %(host)s:%(port)s" % self.__dict__)
    def start(self):
        self._server = eventlet.spawn(eventlet.wsgi.server,
                                      self._socket,
                                      self.app,
                                      protocol=eventlet.wsgi.HttpProtocol,
                                      custom_pool=self._pool)
    def stop(self):
        if self._server is not None:
        self._pool.resize(0)
        self._server.kill()
    def wait(self):
        try:
            self._server.wait()
        except greenlet.GreenletExit:
            print("WSGI server has stopped.")
這個類的功能是實現對執行緒的建立和管理。

(5)WSGIService類

service.py

class WSGIService(object):
    def __init__(self):
        self.loader = wsgi.Loader()
        self.app = self.loader.load_app()
        self.server = wsgi.Server(self.app,
                                  '0.0.0.0',
                                  8080)
    def start(self):
        self.server.start()
    def wait(self):
        self.server.wait()
    def stop(self):
        self.server.stop()<pre name="code" class="python">    if __name__ == "__main__":
        server = WSGIService()
        server.start()
        server.wait()
可以看到WSGIService類就是呼叫Server類的start、stop和wait方法。該類定義了主方法,我們可以利用python service.py啟動WSGI服務。

至此,我們分析了應用方法和類的方式通過PasteDeploy來實現的WSGI服務。後面我們將繼續分析OpenStack中WSGI。