欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Nova Conductor服务源码分析

程序员文章站 2022-05-11 17:25:53
...
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模块目录结构如下:
[[email protected] 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))
相关标签: nova