Nova Conductor服务源码分析

Conductor服务nova-conductor最初于Grizzly版本发布,目的是为数据库访问提供一层安全机制。
在此之前,nova-compute都是直接访问数据库,一旦被攻破,则数据库会面临直接暴露的危险。
此外,nova-conductor的加入也使得nova-compute与数据库解耦,因此在保证conductor API兼容性的前提下,数据库schema升级的同时并不需要去升级nova-compute。
nova-conductor对数据库访问的性能也有相应的提高,此前当使用协程时,所有数据库访问都是阻塞的,引入nova-conductor之后,就可以通过创建多个协程使用RPC访问nova-conductor改善这个问题。当然这么做不可避免有一些限制,RPC调用是有延迟的,nova-conductor本身访问数据库也是有阻塞的,当部署nova-conductor实例较少时,阻塞现象会更加突出性能的问题。
目前为止,nova-compute所有访问数据库的动作都要交给nova-conductor完成,出于完全性考虑,应该避免nova-conductor和nova-compute部署在同一服务器上,否则移除数据库直接访问就没有任何意义了,随着nova-conductor服务的不断完善,它还需要承当部分原本由nova-compute负责的TaskAPI任务,TaskAPI主要包含耗时较长的任务,比如创建虚拟机,迁移虚拟机等。
Conductor服务的源码位于nova/conductor目录:
├── api.py
├── __init__.py
├── manager.py
├── rpcapi.py
└── tasks
    ├── live_migrate.py
一般来说rpcapi.py文件与RPC相关,其他服务将这个模块导入就可以使用它提供的接口远程调用nova-conductor提供的服务,nova-conductor注册的RPC Server接受到RPC请求后,再由manager.py文件中的类ConductorManager真正地完成数据库访问的操作。但是基于数据库访问的特殊性,api.py文件中又对RPC的调用做了一层封装,其他模块需要导入的是api模块,而不是rpcapi模块。
而数据库访问的特殊性在于需要区分是不是需要通过RPC,api.py里定义了四个类:LocalAPI,API,LocalComputerTaskAPI以及ComputerTaskAPI。前两个类是nova-conductor访问数据库的接口,后两个是TaskAPI接口。
nova-conductor和nova-compute部署在一个节点时,并不需要通过RPC调用去访问数据库,此时通过类LocalAPI直接操作数据库,否则,会通过类API使用recapi.py中定义的类ConductorAPI发送RPC请求给nova-conductor远程访问数据库。同样对于TaskAPI任务,nova-api和nova-conductor部署在一起时,会使用类LocalComputerTaskAPI,也不需要经过RPC调用。
# nova/conductor/__init__.py
from nova.conductor import api as conductor_api
def API(*args, **kwargs):
    #根据配置选项use_local的定义判断nova-conductor是否与nova-compute部署在
    #同一个节点上,如果是则使用LocalAPI访问数据库,否则通过API使用RPC访问
    use_local = kwargs.pop('use_local', False)
    if oslo.config.cfg.CONF.conductor.use_local or use_local:
        api = conductor_api.LocalAPI
    else:
        api = conductor_api.API
    return api(*args, **kwargs)
def ComputeTaskAPI(*args, **kwargs):
    use_local = kwargs.pop('use_local', False)
    if oslo.config.cfg.CONF.conductor.use_local or use_local:
        api = conductor_api.LocalComputeTaskAPI
    else:
        api = conductor_api.ComputeTaskAPI
return api(*args, **kwargs)
对于数据访问,无论是本地访问或者通过远程调用,最终真正完成数据操作的都是manager.py里的类ConductorManager。对于nova-conductor提供的TaskAPI功能,目前只有Nova API服务会用到,同样无论是否通过RPC远程调用,最终完成任务都是manager.py里的类ComputerTaskManager,而部分TaskAPI任务,会由ComputerTaskManager调用nova/conductor/tasks目录的模块完成。
#nova/conductor/manager.py
class ConductorManager(manager.Manager):
    @messaging.expected_exceptions(exception.InstanceNotFound)
    def instance_get_by_uuid(self, context, instance_uuid,
                             columns_to_join):
        return jsonutils.to_primitive(
            self.db.instance_get_by_uuid(context, instance_uuid,
                columns_to_join))
ConductorManager继承自nova. manager.Manager,而nova. manager.Manager又继承自nova.db.base.Base,类Base会根据配置选项db_drive的定义导入相应数据库操作模块,默认为nova.db模块。
nova.db模块目录结构如下:
[root@localhost db]# tree -L 1
.
├── api.py
├── base.py
├── __init__.py
├── migration.py
└── sqlalchemy
api.py提供了数据库对外接口。
#nova/nova/db/api.py
def service_create(context, values):
    """Create a service from the values dictionary."""
    return IMPL.service_create(context, values)
而sqlalchemy/api.py文件提供了对这些接口真正的实现。
#nova/db/sqlalchemy/api.py
@require_admin_context
def service_create(context, values):
    service_ref = models.Service()
    service_ref.update(values)
    if not CONF.enable_new_services:
        service_ref.disabled = True
    try:
        service_ref.save()
    except db_exc.DBDuplicateEntry as e:
        if 'binary' in e.columns:
            raise exception.ServiceBinaryExists(host=values.get('host'),
                        binary=values.get('binary'))
        raise exception.ServiceTopicExists(host=values.get('host'),
                        topic=values.get('topic'))
    return service_ref
sqlalchemy子目录下的models.py文件定义了每一个类都对应数据库的一张表。
#nova/db/sqlalchemy/models.py
class Certificate(BASE, NovaBase):
    """Represents a x509 certificate."""
    __tablename__ = 'certificates'
    __table_args__ = (
        Index('certificates_project_id_deleted_idx', 'project_id', 'deleted'),
        Index('certificates_user_id_deleted_idx', 'user_id', 'deleted')
    )
    id = Column(Integer, primary_key=True)
    user_id = Column(String(255))
    project_id = Column(String(255))
    file_name = Column(String(255))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值