libcloud代码研究(一)——基本架构
libcloud是apache下整合多种云服务接口的项目。最近,在研究libcloud代码的同时,将阿里云存储(Ali OSS)和百度云存储用libcloud storage driver规范进行封装,相当于在libcloud中添加了对阿里云存储和百度云存储的支持。
libcloud中对其支持的每个云存储,都提供response、rawresponse、connection和storagedriver类。每个云存储相应类都由诸多基类继承而来,能最大限度重用代码,发掘多个云存储间相似之处。
libcloud中诸多云存储大致分为两类规范,两类规范的代表分别为cloudfiles和Amazon S3。
CloudFiles | Amazon S3 | |
返回信息格式 | JSON | XML |
数据组织形式 | container + object | bucket + object |
认证方式 | OAUTH认证(获取token) | 请求签名 |
除这些之外,还有诸多细节上区别,比如请求头必须包含内容、container(bucket)和object的命名大小限制,还有返回状态码等等,不一一赘述。
Openstack swift即是典型的CloudFiles式规范,而Google Storage和本人封装的Ali OSS遵循的则是Amazon S3式规范。
下面以CloudFiles和Amazon S3这两种代表性云存储为例,解析libcloud相应代码结构。
1. 响应类(Response和RawResponse)
![](https://images0.cnblogs.com/blog2015/579858/201508/241047035611392.png)
Response类接收httplib.HTTPResponse类对象和下一部分即将要介绍的Connection类对象作为初始化参数。获取HTTP响应信息中返回码、错误信息等,并提供对body的解析方法。
为何要为返回信息单独派生一个RawResponse类呢?我的理解是在GET、PUT请求存取数据时,此时response body内容非空,使用HTTPConnection.send()上传body内容,而send()要求在endheaders()和getresponse()之间调用。Response类被调用流程不能满足此点,故派生类RawResponse。至于使用HTTPConnection.send()上传body数据,而不直接在request中指定body参数上传数据的原因,我想应是为了便于大数据分块上传,代码易于扩展。
针对XML和JSON两种不同的返回信息格式,派生不同的子类,并分别重定义相应body解析方法。而后,根据S3和CloudFiles自身对返回信息规范,再次派生出相应的子类,用于处理解析各自HTTP response。
2. 连接类(Connection)
![](https://images0.cnblogs.com/blog2015/579858/201508/241047198434933.png)
connection是所有连接类的基类,它根据secure参数选择HTTP或HTTPS协议,建立到云端的连接。在类图中只列出了其较为重要的属性与方法。
对某一云存储的具体connection,会根据其具体规范为headers和params赋值,抑或重定义request()函数。
这里想说说Openstack中鉴权,鉴权过程在OpenStackBaseConnection类中重定义的morap_action_hook()函数中调用的_populate_hosts_and_request_paths (libcloud.common.openstack line. 262--300)中实现。大致流程为,先获取认证类,指定认证api版本,后执行认证过程:
osa = osa.authenticate(**kwargs),以此获得token和相应endpoint。
3. 存取驱动类(StorageDriver)
![](https://images0.cnblogs.com/blog2015/579858/201508/241047300774725.png)
libcloud中对云存储的访问,直接就是通过相应的StorageDriver进行的,对应的Response和Connection类都是为StorageDriver服务。
StorageDriver类本身,定义了通用的StorageDriver结构,但其中具体的RESTful风格方法均未实现,在其子类中(BaseS3StorageDriver、CloudFilesStorageDriver)分别实现。