OpenStack中的RESTful API是如何实现的?

2018-01-31 12:59:28 浏览数 (1)

OpenStack作为一个开源的IaaS平台,各个组件和服务之间的消息传递都是通过RESTfulAPI和RPC传递,这里主要讲讲它是如何实现REST的。由于大家可能对OpenStack这个云平台不熟悉,稍微引用一段文字介绍一下OpenStack。

OpenStack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。 OpenStack是一个开源的云计算管理平台项目,由几个主要的组件组合起来完成具体工作。OpenStack支持几乎所有类型的云环境,项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenStack通过各种互补的服务提供了基础设施即服务(IaaS)的解决方案,每个服务提供API以进行集成。 OpenStack云计算平台,帮助服务商和企业内部实现类似于 AmazonEC2 和S3 的云基础架构服务(Infrastructureas a Service, IaaS)。

众所周知,REST定义中把所有事物都定义为资源,每一个资源对应一个独有的ID,在OpenStack中每一个资源也对应一个UUID。而REST的另一大特点是在于要求应用程序采用标准的方法,不同的HTTP方法都有明确的定义。下表展示了RESTfulAPI中常用的URL。

表中的{instance_id}就是虚拟机的UUID。

相信通过上表可以明确的知道OpenStack中发送请求的url是什么样子的,接下来我会说说它是如何具体实现的。

Part One

OpenStack上所有的web服务都是通过WSGI来部署的,WSGI(Python Web Server Gateway Interface)是Python应用程序或框架和Web服务器之间的一种接口,用一张图片来简单说明一下它的作用,不做过多解释(说清楚WSGI又可以写一篇经验了)。

从图片中可以看出WSGI充当着web应用和服务器之间的粘合剂。

实现RESTful API需要实现URL的映射,而这个功能的实现是依赖于Mapper和Controller两个类,顾名思义Mapper就是做映射,根据用户请求的URL及其方法来确定处理的方法,而Controller中主要是实现了请求的各种方法。

Part Two

首先介绍一下映射类是如何一步步实现的。

URL映射需要在WSGI中增加一个应用,是通过修改WSGI的配置文件paste.ini来完成的,需要增加以下几行:

代码语言:javascript复制
[pipeline:main]pipeline = test[app:test]paste.app_factory = routers:app_factory

可以看出增加了一个名为test的应用,对应的是routers包下的app_factory方法。routers包是OpenStack自己定义的包,找到app_factory可以看到其代码如下:

代码语言:javascript复制
def app_factory(global_config, **local_config):        return Routers()

这里运用到了设计模式中的工厂模式,返回的是一个类的实例。而根据python语法,一个类能够被调用就必须实现自身的call方法,所以接下来是查看Routers类的call方法的代码:

代码语言:javascript复制
class Routers(object):         @wsgify(RequestClass=webob.Request)         def__call__(self, request):                   return self._router

call很简单只是返回了一个router对象,而该对象的初始化时在Routers类的_init函数中:

代码语言:javascript复制
class Routers(object):         def __init__(self):                   #创建Mapper对象用于解析URL                   self.mapper= routes.Mapper()                   #向Mapper对象添加URL映射                   self.add_routes()                   #创建RoutesMiddleware对象,用于URL分发                   self._router = routes.middlerware.RoutesMiddleware(self._dispatch, self.mapper)

相信通过注释读者能够大概读懂这个函数的作用,首先初始化一个mapper成员变量,值为routes包下的Mapper标准类,主要作用是URL解析。然后调用了Routers类的add_routes方法在Mapper对象中注册URL映射。接下来是初始化_router成员变量,该变量是一个RoutesMiddleware对象,功能是将HTTP请求进行分发。初始化需要_dispatch和mapper两个参数,mapper刚才已经提到,_dispatch是Router类的静态方法,功能就是进行请求分发。

因此,大致流程是首先mapper对象进行解析,然后_dispatch进行分发。

下面看一下两个重要的方法的代码:

(1) addroutes方法

代码语言:javascript复制
class Router(object):           def add_routes(self):                    #调用处理HTTP请求的方法的controller对象                    controller = controllers.Controller()                    #添加POST /instances URL映射                    self.mapper.connect(“/instances”,                                       controller = controller, action = “create”,                                       conditions = dict(method=[“POST”]))                    #添加GET /instances URL映射                    self.mapper.connect(“/instances”,                                       controller = controller, action = “index”,                                       conditions = dict(method=[“GET”]))                    #添加GET /instances/{instance_id}URL映射                    self.mapper.connect(“/instances/{instance_id}”,                                       controller = controller, action = “show”,                                       conditions = dict(method=[“GET”]))                    #添加PUT /instances/{instance_id}URL映射                    self.mapper.connect(“/instances/{instance_id}”,                                       controller = controller, action = “update”,                                       conditions = dict(method=[“PUT”]))                    #添加DELETE /instances/{instance_id}URL映射                    self.mapper.connect(“/instances/{instance_id}”,                                       controller = controller, action = “delete”,                                       conditions = dict(method=[“DELETE”]))

细心的朋友通过上面这段代码已经发现了细微的区别,这五条URL映射对应的就是文章前面的表格中的五个请求。

(2) _dispatch方法

代码语言:javascript复制
class Router(object);         @staticmethod         @wsgify(RequestClass=webob.Request)         def _dispatch(request):                   #获取URL解析结果                   match = request.environ[‘wsgiorg.routing_args’][1]                   if not match;                            return_err()                   app = match[‘controller’]                   return app

dispatch方法首先获取URL解析结果,如果为空说明URL没有在mapper对象中注册,抛出错误。否则返回URL对应的controller对象,调用controller对象的_call方法来处理HTTP请求。

到此,URL请求的映射已基本完成,接下来是发现Controller类是如何实现的。(由于作者能力所限,有很多细节没有仔细说明,如果对OpenStack RESTful API有兴趣的朋友可以自己亲手实践一下,效果会更佳)。

Part Three

这里依旧是拿Nova中虚拟机创建的相关内容作为例子,接着part two讲,查看Controller类的call方法:

代码语言:javascript复制
class Controller(object):         @wsgify(RequestClass=webob.Request)         def __call__(self,req):                   #获取URL解析结果                   arg_dict= req.environ[‘wsgiorg.routing_args’][1]                   #获取处理的方法                   action= arg_dict.pop(‘action’)                   delarg_dict[‘controller’]                   #搜索controller类中定义的方法                   method= getattr(self, action)                   #调用方法,处理HTTP请求                   result= method(req, **arg_dict)                   #resul为空的话                   if result is None:                           returnwebob.Response(body=’’, status=’204 Not Found’, headerlist=[(‘Content-Type’,                                             ‘application/json’)])                   else:#有返回值的话                           if not isinstance(result, basestring):                                    result = simplejson.dumps(result)         #用json处理方法转化为字符串

对HTTP协议熟悉的同学应该看到了熟悉的内容,值得一提的是这里使用了python的一个自省方法getattr(),传入对象名就可以返回对象的函数,如果没有找到则会返回not found。

接下来,假如HTTP请求时“GET /instances/{instance_id}”,则会对应Controller中的show方法:

代码语言:javascript复制
class Controller(object):         def show(self, req, instance_id):                   inst = self.instances.get(instance_id)                   return {‘instance’: inst}

可以看出调用了show方法之后会返回一个字典,内容是该虚拟机的详细信息。

0 人点赞