OpenStack之Nova分析——Nova Scheduler服务启动

在继续介绍Nova虚拟机创建过程之前,我们来穿插一个很重要的内容,这就是Nova组件的“大脑”——Nova Scheduler(调度器)。它是我们后续分析虚拟机创建的基础。这个子服务的作用前面已经介绍过,从其名称也可以看出该子服务就是负责调度各子服务工作的。

Nova Scheduler属于RPC服务(至于什么是RPC服务可以关注我之前的文章),这篇文章我们先来分析一下Nova Scheduler的启动流程。

Nova Scheduler服务的启动脚本位于nova/bin/nova-scheduler文件

#加载配置项
CONF = cfg.CONF
CONF.import_opt('scheduler_topic','nova.scheduler.rpcapi')
if __name__ == '__main__':
    config.prase_args(sys.argv)  #加载控制台传入的参数
    logging.setup("nova")        #配置服务的日志文件
    #定义Nova Scheduler服务对象
    server = service.Service.create(binary='nova-scheduler',topic=CONF.scheduler_topic)
    service.serve(server)  #创建线程运行Nova Scheduler服务
    service.wait()  #启动Nova Scheduler服务线程

import_opt方法首先会加载nova.scheduler.rpcapi模块,然后再检查模块中是否定义了scheduler_topic配置项。查看该模块的代码。

rpcapi_opts = [cfg.StrOpt('scheduler_topic',
                          default='scheduler',
                          help='the topic scheduler nodes listen on'),]

可以看到nova.scheduler.rpcapi模块中定义了scheduler_topic的默认值为scheduler。

注:在Nova文件的许多地方都定义了一些配置项的默认值,可以在nova.conf文件中做相应的配置来覆盖默认值

Nova Scheduler服务的启动代码加载了控制台传入的参数,例如我们通过命令:python /opt/stack/novabin/nova-scheduler --config-file=/etc/nova/nova.conf来启动Nova Scheduler服务,这里的控制台传入的参数就是配置文件的路径。

接下来调用service包中Service类的create方法创建Service对象,其中binary是服务名,topic是Nova Scheduler服务监听的RPC主题。然后分别调用了service包的serve和wait方法,创建和启动Nova Scheduler服务线程。

下面来详细分析一下service这个包。上述服务的启动过程主要用到了service包的Service类的create方法以及service包的serve和wait方法,分别来看一下。

1. Service类的create方法

class Service(object): 
    @classmethod
    def create(cls, host=None, binary=None, topic=None, manager=None,
               report_interval=None, periodic_interval=None,
               periodic_fuzzy_delay=None):
        if not host:
            host = CONF.host
        if not binary:
            binary = os.path.basename(inspect.stack()[-1][1])
        if not topic:
            topic = binary.rpartition('nova-')[2]
        if not manager:
            manager = CONF.get('%s_manager' % topic, None)
        if report_interval is None:
            report_interval = CONF.report_interval
        if periodic_interval is None:
            periodic_interval = CONF.periodic_interval
        if periodic_fuzzy_delay is None:
            periodic_fuzzy_delay = CONF.periodic_fuzzy_delay
        service_obj = cls(host, binary, topic, manager,
                          report_interval=report_interval,
                          periodic_interval=periodic_interval,
                          periodic_fuzzy_delay=periodic_fuzzy_delay)

        return service_obj
这个方法很简单,首先检查一些参数设置,如果没有设置则采用默认值,最后返回一个Service对象。Service类的初始化方法做了两件事——保存create方法传入的参数并创建所需的API对象,等待Conductor服务运行。

2. serve方法

def serve(server, workers=None):
    #设置_laucher全局变量,以保证每个进程只能启动一个服务
    global _launcher
    if _launcher:
        raise RuntimeError(_('serve() can only be called once'))

    #如果传入了workers参数,则启动(workers)个线程运行服务
    if workers:
        _launcher = ProcessLauncher()
        _launcher.launch_server(server, workers=workers)
    else:
        _launcher = ServiceLauncher()
        _launcher.launch_server(server)
serve方法有两个参数,其中server是Service类的create方法创建的Service对象,workers是创建服务的线程数。如果定义了workers,会创建一个ProcessLauncher对象,该对象的launch_server方法会创建workers个线程,每个线程运行一个RPC服务。如果没有定义workers,会创建一个ServiceLauncher对象,该对象的launch_server方法只创建1个RPC服务。

在Nova Scheduler服务的启动脚本中,没有定义workers参数,所以会调用ServiceLauncher对象的launch_server方法。

def launch_server(self, server):
    ...
    gt = eventlet.spawn(self.run_server, server)
    self._services.append(gt)
方法创建了一个绿色线程,该线程会运行ServiceLauncher对象的run_server方法。我们仔细看一下run_server方法,该方法中的server参数是一个Service对象。
def run_server(server):
    server.start()
    server.wait()

可以看到先调用了Service对象的start方法,start方法定义如下

class Service(object):
    def start(self):
        ...
        self.basic_config_check()  #检查服务配置
        self.manager.init_host()  #初始化主机
        self.model_disconnected = False
        ctxt = context.get_admin_context()
        try:
            #查看数据库,获取当前服务的id
            self.service_ref = self.conductor_api.service_get_by_args(ctxt,
                                                                      self.host,
                                                                      self.binary)
            self.service_id = self.service_ref['id']
        except exception.NotFound:
            #如果不存在当前服务记录,则创建新记录
            self._create_service_ref(ctxt)
        ...
        #创建与RabbitMQ服务器的连接
        self.conn = rpc.create_connection(new=True)
        #创建分发RPC请求的RpcDispatcher对象
        rpc_dispatcher = self.manager.create_rpc_dispatcher()
        #创建RPC消费者
        self.conn.create_consumer(self.topic, rpc_dispatcher, fanout=False)
        node_topic = '%s.%s' % (self.topic, self.host)
        self.conn.create_consumer(node_topic, rpc_dispatcher, fanout=False)
        self.conn.create_consumer(self.topic, rpc_dispatcher, fanout=True)
        #激活消费者
        self.conn.consume_in_thread()
        ...
        #创建定时任务
        pulse = self.servicegroup_api.join(self.host, self.topic, self)
        if pulse:
            self.timers.append(pulse)
        ...

这个方法主要完成了两个工作,其一是:创建“消费者”,监听其它模块发来的RPC请求;其二是:创建线程运行的定时任务(这方面我目前还不太了解,后面研究一下吧)。

注:具体的关于RPC消息通信请参看我前面的文章,这里不再赘述。

Service对象的wait方法很简单,其功能就是启动线程运行周期任务。

3. wait方法

serve方法是创建了一个绿色线程,而wait方法的功能是运行线程。

到这里,Nova Scheduler服务启动过程就分析完了。需要说明的是,Nova中全部子服务的启动流程都极其相似,大家可以继续去分析其它子服务的启动流程~~


阅读更多
换一批

没有更多推荐了,返回首页