OpenStack 通用技術有哪些

wblr6035 8年前發布 | 23K 次閱讀 OpenStack WSGI 分布式/云計算/大數據

來自: http://1.chaoxu.sinaapp.com/archives/3858

OpenStack遵循這樣的設計原則,“不要重復發明輪子”,即對已實現的功能,開發者直接拿來用即可。這一設計原則最終形成了一個由專門團隊維護的Oslo——OpenStack公共庫,實現硬件、操作系統和應用程序等的松耦合。

一.消息總線(MQ)

OpenStack的各項目之間通過REST ful API進行通信;項目內部、不同服務進程之間的通信,則必須要通過消息總線。軟件從最初的面向過程、面向對象、再到面向服務,要求我們去考慮各個服務之間如何傳遞消息,借鑒硬件總線的概念,引入了消息總線的模式,顧名思義,一些服務向總線發送消息,其他服務則從總線上獲取消息。

OpenStack對很多消息總線的開源實現提供了支持,比如RabbitMQ、Qpid等。基于這些消息總線類型,OpenStack oslo.messaging庫實現了以下兩種方式來完成項目內部各服務進程之間的通信。

遠程過程調用(RPC)

通過遠程過程調用,一個服務進程可以調用其他服務進程的方法,并且有兩種調用方式,call和cast。通過call的方式調用,遠程方法會被同步執行,調用者會被阻塞直到結果返回。通過cast方式調用,遠程方法會被異步執行,結果并不會立即返回,調用者也不會被阻塞,但是調用者需要利用其他方式查詢這次遠程調用的結果。

事件通知(Event Notification)

某個服務進程可以把時間通知發送到消息總線上,該消息總線上所有對此類事件感興趣的服務進程,都可以獲得此事件通知并進行下一步的處理,處理的結果并不會返回給事件發送者。這種通信方式,不但可以在同一個項目內部的各個服務進程之間發送通知,也可以實現跨項目之間的通知發送。Ceilometer就通過這種方式大量獲取其他OpenStack項目的事件通知,從而進行計量和監控。

1.AMQP(高級消息隊列協議)

OpenStack支持的消息總線類型中,大部分都是基于AMQP的。AMQP是一個異步的應用層消息傳遞開放協議,主要包括了消息的導向、消息交換、消息隊列和路由。對于一個使用了AMQP的中間件服務而言,當不同的消息由生產者(Producer)發送到Server時,它會根據不同的條件把消息傳遞給不同的消費者(Consumer)。如果消費者無法接收消息或者接收消息不夠快時,它會把消息緩存在內存或者磁盤上。

2.基于AMQP實現RPC

3.OpenStack支持的常見消息總線類型

1)RabbitMQ

2)Qpid

3)ZeroMQ

二.SQLAlchemy和數據庫(Shane)

SQLAlchemy是Python語言下的一款開源軟件,提供了SQL工具包以及對象關系映射器ORM,這樣SQLAlchemy便能讓Python開發人員簡單靈活地運行SQL操作后臺數據庫。

SQLAlchemy主要分成兩部分,SQLAlchemy Core和SQLAlchemy ORM。前者主要包括SQL語言表達式、數據庫引擎、連接池等,其目的是為了實現連接不同的后臺數據庫、提交查詢和更新SQL請求去后臺執行。SQLAlchemy ORM提供數據映射模式,也就是把程序語言的對象數據映射成數據庫中的關系數據,或者把關系數據映射成對象數據。

需要注意的是,如果程序用了對象關系映射器,雖然好處極多,但程序性能會受到一定影響。因此,對象關系映射是一個可選的模塊,而開發人員即便不用任何對象關系映射也能直接用SQLAlchemy操作數據。

三.RESTful API和WSGI

OpenStack項目都是通過RESTful API向外提供服務,這使得OpenStack的接口在性能、可擴展性、可移植性、易用性等方面做到了比較好的平衡。

1.什么是REST ful API

如果一個軟件架構符合REST原則,我們就稱它為RESTful架構。

RESTful架構的一個核心概念是“資源”。從RESTful的角度看,網絡中的任何東西都是資源。它可以是一段文本、一張圖片、一首歌曲、一種服務等,每個資源都對應于一個特定的URL,并用它進行標示,訪問這個URL就可以獲得此資源。

資源可以有多種具體的表現形式,也就是資源的“表述”(Represent),比如一張圖片可以是JPEG格式也可以是PNG格式,URL代表的是資源實體,而不是表現形式。

客戶端和服務端之間的互動傳遞就是資源的表述。我們上網瀏覽網頁,就是在調用資源的URL,獲取它不同表現形式的過程,這種互動只能使用無狀態協議HTTP,也就是說,服務端必須保存所有的狀態,客戶端可以使用包括GET、POST、PUT和DELETE這些在內的基本操作,使服務端上的資源發生“狀態轉移”,也就是所謂的“表述性狀態轉移”

2.RESTful路由

Openstack各個項目都提供了RESTful架構的API作為對外提供的接口,而RESTful架構的核心是資源和資源上的操作。也就是說,OpenStack定義了很多的資源,并實現了針對這些資源的各種操作函數。

當OpenStack 的API服務進程接收到客戶端的HTTP請求時,一個所謂的路由模塊會將請求的URL轉換成相應的資源,并路由到合適的操作函數上。OpenStack使用的路由模塊是Routers。

比如,當我們執行“neutron router-list”命令時,Neutron客戶端(neutron_client)將這個命令轉換成HTTP請求發送給Neutron的server服務進程,然后被路由到下面代碼中的“index”操作。

neutron/api/v2/router.py

43 class Index(wsgi.Application): 44 def init(self, resources): 45 self.resources = resources 46 47 @webob.dec.wsgify(RequestClass=wsgi.Request) 48 def call(self, req): 49 metadata = {'application/xml': {'attributes': { 50 'resource': ['name', 'collection'], 51 'link': ['href', 'rel']}}} 52 53 layout = [] 54 for name, collection in self.resources.iteritems(): 55 href = urlparse.urljoin(req.path_url, collection) 56 resource = {'name': name, 57 'collection': collection, 58 'links': [{'rel': 'self', 59 'href': href}]}</pre>

3.什么是WSGI

RESTful只是軟件設計的風格而不是標準,Web服務中通常使用基于HTTP的符合RESTful風格的API。而WSGI(Web服務器網關接口)則是Python語言中所定義的Web服務器和Web應用程序之間的通用接口標準。

WSGI是一個網關,作用是在協議之間進行轉換。也就是說,WSGI是一座橋梁,橋梁的一端稱為服務端或者網關端,另一端稱為應用端或者框架端。當處理一個WSGI請求時,服務端為應用端提供上下文信息和一個回調函數,應用端處理完請求之后,使用服務端提供的回調函數返回相對應的請求響應。

作為一個橋梁,WSGI將Web組件分成了三類,Web服務器(WSGI Server)、Web中間件(WSGI Middleware)與Web應用程序(WSGI APPlication)。WSGI Server接收http請求,封裝一系列的環境變量,調用注冊的WSGI Application,最后將響應返回給客戶端。

WSGI Application是一個可被調用的Python對象,它接受兩個參數,通常為environ和start_response。如下一個Neutron項目中關于Server的功能測試列子:

neutron/tests/functional/test_server.py

177 def application(environ, start_response): 178 """A primitive test application.""" 179 180 response_body = 'Response' 181 status = '200 OK' 182 response_headers = [('Content-Type', 'text/plain'), 183 ('Content-Length', str(len(response_body)))] 184 start_response(status, response_headers) 185 return [response_body]</pre>

其中,參數environ指向一個Python字典,WSGI Application可以從environ字典中獲取相對應的請求及其執行上下文的所有信息。而參數start_response指向一個回調函數,如下所示一個Neutron項目中關于WSGI的單元測試列子:

vim neutron/tests/unit/test_wsgi.py

161 def hello_world(env, start_response): 162 if env['PATH_INFO'] != '/': 163 start_response('404 Not Found', 164 [('Content-Type', 'text/plain')]) 165 return ['Not Found\r\n']</pre>

字符串“404 Not Found”用于表示請求響應的狀態,“’Content-Type’, ‘text/plain’”是一個分別代表了header_name,header_value的元組列表。也就是HTTP響應中的http報頭和內容。

WSGI中間件同時實現了服務端和應用端的API,因此可以在兩端之間起協調溝通作用,在服務端看來,中間件就是一個WSGI應用;在應用端看來,中間件則是一個WSGI服務器。

WSGI中間件可以將客戶端的HTTP請求,路由給不同的應用對象,然后將應用處理后的結果返回給客戶端。自然,我們也可以將WSGI中間件理解為服務端和應用端交互的一層包裝,經過不同中間件的包裝,便具有不同的功能,比如URL路由分發,再比如權限認證,這些不同中間件的組合便形成了WSGI的框架,比如Paste。

4.什么是Paste

OpenStack使用Paste的Deploy組件來完成WSGI服務器和應用的構建,每個項目源碼的etc目錄下都有一個Paste配置文件,比如Neutron中的/etc/neutron/api-paste.ini,部署時,這些配置文件會被復制到系統的/etc/Project_name目錄下。Paste Deploy的工作便是基于這些配置文件。

Paste配置文件分為多個Section,每個Section以type:name的格式命名。使用Paste Deploy的主要目的是從配置文件中生成一個WSGI Application,有了配置文件之后,只需要使用下面的調用方式。

from paste.deploy import loadapp
wsgi_app = loadapp(‘config:/path/to/config.ini’)

5.什么是WebOb

除了Routers與Paste Deploy外,OpenStack中另一個與WSGI密切相關的是Webob。Webob通過對WSGI的請求與響應進行封裝,來簡化WSGI應用的編寫。

6.什么是Eventlet

Openstack中的絕大部分項目都采用了協程模型。在操作系統看來,一個Openstack服務只會運行在一個進程中,但在這個進程中,OpenStack巧妙的地利用了Python的網絡庫Eventlet產生出許多個協程(綠色線程),這些協程之間只有在調用到了某些特殊的Eventlet庫函數的時候(比如睡眠sleep、I/O調用等)才會發生切換。

與線程類似,協程也擁有自己獨立的棧和局部變量,同時,又與其他協程共享全局變量。協程與線程的主要區別是,多個線程可以同時運行,而同一時間內只能有一個協程在運行,無須考慮很多鎖的問題。

使用線程時,線程的執行完全由操作系統控制,而使用協程時,協程的執行順序、時間完全由程序自己決定,由于工作方式為主動模式,所以可以最大化的利用CPU性能。協程的實現主要是在協程休息時把當前的寄存器保存起來,然后重新工作時再將其恢復過來,因此,協程可以理解為一個線程內的偽并發方式(并發也就是指創建多個綠色線程)。

7.什么是AsynclO

目前,OpenStack社區正在考慮使用AsyncIO來代替Eventlet。AsyncIO可以看做是許多第三方Python庫的超集,包括Twisted、Eventlet等。

三.OpenStack通用庫Oslo

1.Cliff

Cliff,是OpenStack中用來幫助構建命令行程序的通用庫。主程序負責基本命令行參數的解析,然后調用各個子命令去執行不同的操作。

2.oslo.config

oslo.config通用庫用于解析命令行和配置文件中的配置選項。

3.oslo.db

oslo.db是針對SQLAlchemy訪問的對象。

4.oslo.i18n

oslo.i18n是對Python gettext模塊的封裝,主要用于openstack字符串的翻譯和國際化。

5.oslo.messaging

oslo.messaging通用庫為openstack各個項目使用RPC和事件通知提供了一套統一的接口。

為了支持不同的RPC后端實現,oslo.messaging對如下的對象進行了統一:

Transport

Transport傳輸層主要實現RPC底層的通信,比如Socket以及事件循環、多線程等其他功能。

Target

Target封裝了指定某一個消息最終目的地的所有消息。

Server

一個RPC服務器可以暴漏多個endpoint,每個endpoint包含一組方法。

RPC Client

通過RPC Client可以遠程調用RPC Server上的方法,有cast和call兩種遠程調用方式。

Stevedore

在Python代碼運行時動態發現和載入所謂的插件“Plugin”,使得程序更容易的擴展,Python庫Stevedore就是在setuptools的entry points基礎上,構造了一層抽象層。使用Stevedore實現程序動態載入插件的過程主要分為三個部分:插件的實現;插件的注冊,以及插件的載入。

Taskflow

通過Taskflow通用庫,可以更容易的控制任務(Task)的執行。Task、flow和engine是Taskflow中的幾個基本概念。

Task是Taskflow庫中擁有執行(execute)和回滾(revert)功能的最小單位(實際最小單位是atom,其他所有類包括task類都是Atom類的子類),然后新建一個線性流flow。而engine用來載入一個flow,然后驅動該flow中的task/flow運行。

Cookiecutter

開發人員,可以使用openstack的Cookiecutter通用庫模板,新建一個符合慣例的openstack項目。

Oslo.policy

Policy用于控制用戶的權限,能夠執行什么樣的操作,openstack的每個項目中都有一個/etc/project_name/policy.json文件,可以通過配置該文件來實現對用戶的權限管理。將policy操作的公共部分提取出來,便形成了oslo.policy通用庫。

Oslo.rootwrap

Oslo.rootwrap可以讓openstack服務以root身份去執行一些shell命令。一般來說openstack的服務都是以普通用戶的身份去運行的,但是當他們需要以root身份去執行一些shell命令時,便需要利用到oslo.rootwrap的功能。

Oslo.test

Oslo.test庫是openstack中提供單元測試的基礎框架或通用庫。Oslo.test基于testtools庫定義了oslotest.base.BaseTestCase類,可以作為其他openstack項目單元測試的基類。

比如,在下面的neutron/tests/unit/api/v2/test_resource.py文件中:

28 class RequestTestCase(base.BaseTestCase):
 29     def setUp(self):
 30         super(RequestTestCase, self).setUp()
 31         self.req = wsgi_resource.Request({'foo': 'bar'})
 32
 33     def test_content_type_missing(self):
 34         request = wsgi.Request.blank('/tests/123', method='POST')
 35         request.body = b"<body />"
 36         self.assertIsNone(request.get_content_type())
 37
 38     def test_content_type_with_charset(self):
 39         request = wsgi.Request.blank('/tests/123')
 40         request.headers["Content-Type"] = "application/json; charset=UTF-8"
 41         result = request.get_content_type()
 42         self.assertEqual(result, "application/json")

使用BaseTestCase作為基類時,單元測試中創建的所有臨時文件都會被存放在一個單獨的目錄中,此時系統環境變量HOME也會被設置成一個臨時的目錄。

BaseTestCase類提供了方法create_tempest()來創建臨時文件:

Create_tempfiles(files, ext=’.conf’)
參數:files(元組的列表),包含了類似(文件名、文件內容)的元組列表
      ext(字符串),新建的文件擴展名
返回:所有新建的文件名列表

oslo.test通用庫除了提供上面的基類之外,還提供了兩個通用的fixture,oslotest.mockpatch和oslotest.moxstubout供其他Openstack項目開發單元測試用例。不過,一般建議使用oslotest.mockpatch。比如:

Oslotest通用庫提供了一個debug腳本用來支持在測試中使用pdb。

1)在代碼中設置調試:

import pdb; pdb.set_trace()

2)在Openstack項目的tox.ini配置文件中加入如下行:

[ testenv:debug]
commands = oslo.debug_helper.sh {posargs}

3)運行類似下面的命令來觸發斷點,進入Python debugger:

tox –e debug

tox –e debug 程序模塊名.類名.方法名</pre>

 本文由用戶 wblr6035 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!