Nova Conductor 与 Versioned Object Model 机制
2019-07-23 17:48 云物互联 阅读(390) 评论(0) 编辑 收藏 举报目录
Nova Conductor
Conductor 服务作为 Nova 核心部件之一最初在 Grizzly 版本中发布,在整个 Nova 中充当着组织者的角色。主要提供了 3 个功能:
-
nova-conductor 连接了 nova-api、nova-compute 和 nova-scheduler 服务,提供了 长时任务编排(Task Orchestration)功能。Nova 将所有耗时长,跨节点,易出错但相对固定的处理流程抽象成 Task,包括启动虚拟机(schedule_and_build_instances)、冷迁移(migrate_server)和热迁移(live_migrate_instance)等。Conductor 作为 Task 的组织者,在执行 Task 的时候 Conductor 会一直追踪它的状态,并且能够执行错误处理、恢复等一系列工作。
-
nova-conductor 为 nova-computee 提供了 数据库的代理访问机制。因为计算节点是 OpenStack 云环境中与用户业务接触最近、数据最多、最容易被攻击的目标,所以 nova-compute 被设计成无法直接访问数据库,以此来保障数据库的安全。
-
nova-conductor 为版本较旧的 nova-compute 提供了 数据库版本向下兼容性。OpenStack 作为一个分布式系统,不同的服务可能运行在不同的代码版本上,在这些模块通信的过程中,Conductor 为传输的数据对象提供了版本兼容功能。让处于不同版本的 Nova 服务能够识别 RPC 请求中数据对象的版本,并完成不兼容的对象转换。但需要注意的是,这种兼容性是向下的。
Conductor 的代码包目录结构:
[root@localhost nova]# tree conductor/
conductor/
├── api.py
├── __init__.py
├── manager.py
├── rpcapi.py
└── tasks
├── base.py
├── __init__.py
├── live_migrate.py
└── migrate.py
其中,在 manager 模块中主要实现了 ComputeTaskManager 任务编排封装类和 ConductorManager 数据库访问代理封装类。
数据库访问代理机制
Conductor 实现的数据库访问代理机制带来的好处:
- 为 nova-compute 服务的数据库访问提供了一层额外的安全保障。
- 在保证 Conductor API 兼容性的前提下,数据库 schema 升级的同时不需要升级 nova-compute 的代码版本。
- nova-compute 可以通过创建多个协程来使用非阻塞的 nova-conductor RPC 调用。
需要注意的是,nova-conductor 作为代理服务成为了 nova-compute 访问数据库速度的瓶颈,好在由于 Conductor 服务是无状态服务,所以在性能和稳定性要求较高的环境中可以任意横向扩展 Conductor 节点的数量。
Versioned Object Model 机制
Versioned Object Model(版本化对象模型)机制在 Icehouse 版本被引入。Object Model 将每一张数据库表都与一个 Object 对应,将对数据库表的操作都封装到 Object 实例中,需要通过 Object 实例来进行数据库的操作。这听起来了 ORM 很相似,但实际上 Object Model 是比 ORM 更上一层的封装。ORM 只是单纯将数据库表结构映射成为了一个 Python 对象,但 Object Model 是在此之上做了更多的数据库操作实践和优化,Object Model 使用面向对象的思想对数据进行了封装,并为封装的数据提供了数据库代理、RPC 向下版本兼容、流量优化等一些列高级特性。
-
Nova 数据库访问方式与对象构建过程解耦:Object Model 支持 remote 和 local 两种数据库访问方式,并非成功构建了 Object 就可以直接操作数据库,在 nova-compute 中通一个 Object 来执行数据库操作实际上是远程的,是一种间接的数据库访问。这是实现数据库访问代理功能的关键。
-
nova-compute 和数据库的在线升级:nova-compute 通过 RPC 传递 Object 到 nova-conductor 时,Object 会维护一个版本号。假设 nova-compute 传递的是一个低版本的 Object,那么 nova-conductor 就会对该 Object 进行版本兼容处理,使得低版本的 Object 也能如常对高版本 DB schema 进行操作。
-
对象属性类型的声明:Python 是一门动态的强类型解释语言,Python 不具有对象类型声明语句,这是 Python 轻便简单的根本,也是 Python 难以支撑开发超大型项目的病因。在 Web API 层面,我们可以通过 validation.schema 和 view_builder 来严格规范输入、输出的数据结构。而在后端程序的内部实现,我们则可以通过 Object Model 来实现对 Object 每个属性字段的类型声明,这帮助我们编写出来的程序具有更好的可读性和稳定性。
-
减少写入数据库的数据量:Object Model 支持增量更新数据库记录属性值,而无需将整个对象的所有属性都更新一遍,每个 Object 都具有一个
_change_field
实例属性用来记录变化的值。
def remotable(fn):
"""Decorator for remotable object methods."""
@six.wraps(fn)
def wrapper(self, *args, **kwargs):
ctxt = self._context
if ctxt is None:
raise exception.OrphanedObjectError(method=fn.__name__,
objtype=self.obj_name())
if self.indirection_api:
# indirection_api: 间接数据库访问 API,就是 conductor_rpcapi.ConductorAPI()
# object_action: remotable 装饰器中调用,Versioned Object 实例通过这个方法来远程访问数据库
# 相对的,object_class_action_versions: remotable_classmethod 装饰器中调用,Versioned Object 类通过这个方法来远程访问数据库
updates, result = self.indirection_api.object_action(
ctxt, self, fn.__name__, args, kwargs)
for key, value in six.iteritems(updates):
if key in self.fields:
field = self.fields[key]
# NOTE(ndipanov): Since VersionedObjectSerializer will have
# deserialized any object fields into objects already,
# we do not try to deserialize them again here.
if isinstance(value, VersionedObject):
setattr(self, key, value)
else:
setattr(self, key,
field.from_primitive(self, key, value))
# obj_reset_changes 重新设置 change 数据
# 相对的,obj_get_changes 获取 change 数据
self.obj_reset_changes()
# _changed_fields 存放增量更新的数据
self._changed_fields = set(updates.get('obj_what_changed', []))
return result
else:
return fn(self, *args, **kwargs)
wrapper.remotable = True
wrapper.original_fn = fn
return wrapper