在Nova Scheduler的启动脚本中,最终的代码是创建Service类的create方法,以及service包的serve和wait方法。
分析Nova Scheduler服务的启动流程。
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类名
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对象
service_obj = cls(host, binary, topic, manager,
report_interval=report_interval,
periodic_interval=periodic_interval,
periodic_fuzzy_delay=periodic_fuzzy_delay)
return service_obj
create方法首先检查一些参数设置,如果在方法调用时没有设置则采用默认值,最后
返回一个Service对象。
Service类的初始化方法做了两个工作:
(1) 保存create方法传入的参数
(2) 创建所需的API对象,并等待Conductor服务正常运行。注意:所有的数据库操作都是通过Nova Conductor服务完
成。因此,其他服务的正常运行,都依赖于Nova Conductor服务已经正常运行
2、serve方法
def serve(server, workers=None):
#设置_laucher全局变量,以保证每个进程只能启动一个服务
global _launcher
if _launcher: #_launcher不为None,说明有服务启动了
raise RuntimeError(_('serve() can only be called once'))
#workers指定启动的服务个数
#如果传入了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个线程,每个线
程运行一个Nova RPC服务。如果没有定义workers,会创建一个ServiceLauncher对象,该对象的launch_server方法
只创建一个线程运行Nova RPC服务。
在Nova Scheduler服务的启动脚本中,没有定义workers参数。因此serve方法会创建ServiceLauncher对象的
launch_server方法。ServiceLauncher类继承于Launcher类,launcher_server方法定义在Launcher类中。
def launch_server(self, server):
...
gt = eventlet.spawn(self.run_server, server)
self._services.append(gt)
方法创建了一个绿色线程,该线程会运行
ServiceLauncher对象的run_server方法。我们仔细看一下run_server方
法,该方法中的server参数是一个Service对象。
3、wait方法
service包的serve方法创建了一个绿色线程,而wait方法的功能是运行线程。serve方法创建的绿色线程会执行
ServiceLauncher对象的run_server方法。ServiceLauncher类继承于Launcher类,launcher_server方法定义在
Launcher类中。如下:
class Launcher(object):
@staticmethod
def run_server(server):
server.start()
server.wait()
该server参数是一个Service对象。可以看到,run_server方法依次调用了Service对象的start和wait方法。
Service类的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)
#激活RPC消费者
self.conn.consume_in_thread()
...
#创建定时任务,定时报道服务的状态
pulse = self.servicegroup_api.join(self.host, self.topic, self)
if pulse:
self.timers.append(pulse)
#如果允许运行周期任务,设置初始等待时间
if self.periodic_enable:
if self.periodic_fuzzy_delay:
#初始等待时间在0~periodic_fuzzy_delay之间随机选择一个数
initial_delay = random.randint(0, self.periodic_fuzzy_delay)
else:
#如果没有选择periodic_fuzzy_delay,则服务启动后立即运行定时任务
initial_delay = None
#创建线程,运行周期任务
periodic = utils.DynamicLoopingCall(self.periodic_tasks)
periodic.start(initial_delay=initial_delay,periodic_interval_max=self.periodic_interval_max)
self.timers.append(periodic)
Service类start方法主要完成两个工作:
(1)创建RPC消费者,监听其他模块发来的RPC请求
(2)创建线程运行定时任务
1、创建RPC消费者。start方法中,共监听了3个消费者。topic类型的消费者,topic.host类型的消费者,广播类型
的消费者。功能:
2、创建一个RpcDispatcher对象,RpcDispatcher对象的功能是决定将RPC请求交给manager对象的哪个方法处理。
RpcDispatcher对象和manager对象在RPC服务中的地位已在"RPC调用的实现"部分介绍(后面详细读)。
3、创建定时任务线程的工作。start方法共两个线程,一个是线程会定时向数据库更新服务状态,一个是运行自定义
的周期任务。
Service对象的wait方法很简单,其功能就是启动线程运行周期任务。
4、wait方法
serve方法是创建了一个绿色线程,而wait方法的功能是运行线程。
总结:Nova Scheduler服务启动过程就分析完了。需要说明的是,Nova中全部子服务的启动流程都极其相似,可以继
续去分析其它子服务的启动流程!!!