OpenStack 通用技术有哪些

来自: 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}]}

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]

其中,参数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']

字符串“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 程序模块名.类名.方法名
posted @ 2016-05-04 14:33  kx的杂货铺  阅读(618)  评论(0编辑  收藏  举报