专栏首页Python中文社区OpenStack中的RESTful API是如何实现的?

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

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来完成的,需要增加以下几行:

[pipeline:main]pipeline = test[app:test]paste.app_factory = routers:app_factory

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

def app_factory(global_config, **local_config):        return Routers()

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

class Routers(object):         @wsgify(RequestClass=webob.Request)         def__call__(self, request):                   return self._router

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

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方法

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方法

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方法:

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方法:

class Controller(object):         def show(self, req, instance_id):                   inst = self.instances.get(instance_id)                   return {‘instance’: inst}

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

本文分享自微信公众号 - Python中文社区(python-china)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-11-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 理解并自定义HttpHandler

    前言                                      之前从网上找了几篇讲解如何自定义HttpHandler的文章,依葫芦画瓢却一直没...

    ^_^肥仔John
  • JS魔法堂:IE5~9的Drag&Drop API

    一、前言                                  《 HTML5魔法堂:全面理解Drag & Drop API》中提到从IE5开始...

    ^_^肥仔John
  • 弹出式模态窗体选择文本控件

    2006年就要到来了,最近比较忙,很少更新blog,今天发一个模态窗体选择文本控件辞旧迎新.新年在发几个asp.net2.0 webPart控件同各位分享: ...

    张善友
  • [认证授权] 1.OAuth2授权

    1 OAuth2解决什么问题的? 举个栗子先。小明在QQ空间积攒了多年的照片,想挑选一些照片来打印出来。然后小明在找到一家提供在线打印并且包邮的网站(我们叫它P...

    blackheart
  • 别再设计易碎的Web API

    原文作者Mathieu Fenniak在博文中大呼:不要再设计易碎的Web API 了,否则你的合作伙伴或第三方开发者会因此恨你,而离你远去的。他认为,想设计出...

    张善友
  • 在链接前面自动添加favicon 图标(jquery)

    相信你在一些网站上看过类似的东东,就是在一个可点击外链左侧,会有一个目标链接网站的favicon 图标。这一个小小的设置可能对提升用户体验有不少帮助,一些Wor...

    Jeff
  • .Net 跨平台可移植类库正在进行

    [原文发表地址] Cross-Platform Portable Class Libraries with .NET are Happening [原文发表时间...

    张善友
  • 后rtx集成时代

    基本结构:客户端向服务器发送命令(如给目标rtx号码发送提醒、短信、访问组织和用户信息等)。 ? 1.首先要把原生API做一个基础接口封装,将RTXSDK内部的...

    用户1183026
  • 游戏开发之UE4添加角色到场景中

    接着上次继续学习,现在我们已经有了一个场景并且运行了,我们需要添加一个角色到场景中。要这样做,我们必须从UE4的GameFramework类继承它。 一. 创建...

    用户1198337
  • java 邮件发送

    步骤1:pom.xml添加依赖项 1 <!--mail--> 2 3 <dependency> 4 ...

    菩提树下的杨过

扫码关注云+社区

领取腾讯云代金券