Nova-console 服务启动过程分析

原文地址:http://wenku.baidu.com/view/4c24e244f7ec4afe04a1dfec.html

Nova-console 服务启动过程分析
1 nova-console脚本分析
1.1 脚本位置
该脚本为python脚本,位于源码目录nova-2011.3\bin 目录下,该目录还包括nova-api,nova-compute,nova-volume,nova-schedule,nova-network,等服务的启动脚本。
1.2 脚本内容分析
当该脚本单独启动时候(非__import__模块),会执行if __name__ == '__main__':下面的语句。
1. utils.default_flagfile()
调用nova.utils模块获取nova.conf 文件路径,并将其放入sys.argv环境变量中。
2. flags.FLAGS(sys.argv)
调用nova.flags模块中的FlagValues类的__call__函数对sys.argv参数进行分析并保存到字典__dict__中。
__dict__:dict对象,__dict__为对象内置隐式属性放置对象的所有属性
例如:对象C内部有属性S,那么C.S=x 等价于C._ _dict_ _['S']=x
3. logging.setup()
调用nova.log模块设置nova的日志,利用nova.log.NovaRootLogger创建一个logger。
4. server = service.Service.create(binary='nova-console')
调用nova.service模块中Service类的静态函数create创建一个nova-console服务实例,其创建过程详见2.1
5. service.serve(server)
调用nova.service模块中的serve函数启动nova-console服务实例。其创建过程详见2.3
6. service.wait()
调用nova.service模块中的wait函数等待服务实例停止。其创建过程详见2.6
2.nova.service 模块调用分析
2.1 service.Service.create(binary='nova-console')
该模块为Service类的静态函数,当未实例化该类之前就可以调用该函数。其代码分析如下:
1.
if not host:
host = FLAGS.host
if not binary:
binary = os.path.basename(inspect.stack()[-1][1])
if not topic:
topic = binary.rpartition('nova-')[2]
if not manager:
manager = FLAGS.get('%s_manager' % topic, None)
if not report_interval:
report_interval = FLAGS.report_interval
if not periodic_interval:
periodic_interval = FLAGS.periodic_interval
上面几条语句为获取创建实例所需要参数,
a. FLAGS.host定义在nova.flags模块中,为socket.gethostname(),利用socket模块获取主机名称。
b. Binary 为传递进来的参数'nova-console',上一版本未有此参数,而是通过栈获取脚本名称的。
c. topic 获取为console
d. manager为FLAGS.get(console-manager),即定义在nova.flags中的'nova.console.manager.ConsoleProxyManager’
e. report_interval和periodic_interval定义在该模块中,分别为10 和60
2. service_obj = cls(host, binary, topic, manager,report_interval, periodic_interval)
return service_obj
调用Service的__init__函数初始化Service实例service_obj并返回。
2.2 Service .__init__()
1. self.host = host
self.binary = binary#nova-console
self.topic = topic#console
self.manager_class_name = manager# nova.console.manager.ConsoleProxyManager 初始化实例各个参数。
2. manager_class = utils.import_class(self.manager_class_name)
调用nova.utils.import_class载入nova.console.manager.ConsoleProxyManager。并返回ConsoleProxyManager类对象。
代码分析如下:
self.manager_class_name = 'nova.console.manager.ConsoleProxyManager'
mod_str, _sep, class_str = ' self.manager_class_name.rpartition('.')
执行结果如下:
mod_str = ‘nova.console.manager’
_sep = ’=’
class_str = ConsoleProxyManager
3. self.manager = manager_class(host=self.host, *args, **kwargs)
调用2返回的类对象ConsoleProxyManage的__init__函数初始化manager实例
初始化详情见3.1:
4.
self.report_interval = report_interval
self.periodic_interval = periodic_interval
super(Service, self).__init__(*args, **kwargs)
self.saved_args, self.saved_kwargs = args, kwargs
self.timers = []
初始化实例的其他参数,完成初始化。
2.3 service.serve(server)
参数server为Service的实例化。
1. _launcher = Launcher()
调用该模块中的Launcher类,并实例化。
2. _launcher.launch_server(server)
调用launch_server函数装载服务。
3. launch_server(self, server)
调用run_server(server),
4. run_server(server)
调用server.start() ,启动过程见2.4
调用server.wait() ,启动过程见2.5
2.4. server.start()
该语句调用Service类的start()函数
1. self.manager.init_host()
调用nova.console.manager.ConsoleProxyManager.init_host()函数。该函数继续调用
nova.console.xvp. XVPConsoleProxy .init_host()函数。该函数扫描数据库,并重新启动xvp。
2.
self.model_disconnected = False
ctxt = context.get_admin_context()
try:
service_ref = db.service_get_by_args(ctxt,self.host,self.binary)
self.service_id = service_ref['id']
except exception.NotFound:
self._create_service_ref(ctxt)
从数据库中查找本服务类型的索引,如果没有则在数据库中创建。
3. if 'nova-compute' == self.binary:
self.manager.update_available_resource(ctxt)
特殊对待nova-compute服务。
5. self.conn = rpc.create_connection(new=True)
创建一个RPC连接。该RPC为建立的RabbitMQ基础上的。参考4.1
6.
# Share this same connection for these Consumers
self.conn.create_consumer(self.topic, self, fanout=False)
node_topic = '%s.%s' % (self.topic, self.host)
self.conn.create_consumer(node_topic, self, fanout=False)
self.conn.create_consumer(self.topic, self, fanout=True
在上面建立连接的基础上建立三个消费队列,其中两个非fanout类型,一个fanout类型。参考4.3
6. self.conn.consume_in_thread()
在同一个线程中执行三个消费队列并进入wait状态。参考4.4
7.
if self.report_interval:
pulse = utils.LoopingCall(self.report_state)
pulse.start(interval=self.report_interval, now=False)
self.timers.append(pulse)
if self.periodic_interval:
periodic = utils.LoopingCall(self.periodic_tasks)
periodic.start(interval=self.periodic_interval, now=False)
self.timers.append(periodic)
根据参数self.report_interval和self.periodic_interval创建两个LoopingCall实例,并启动,将其放入timers列表中。参考5.1
2.5 server.wait()
该语句调用Service类的wait()函数,直接调用LoopingCall的wait()函数,即执行pulse和periodic的wait()函数。
2.6 service.wait()
service.wait()-->_launcher.wait()->service.wait()->nova.utils. LoopingCall.wait()->event.wait()。
3. ConsoleProxyManager类
3.1 ConsoleProxyManager(manager.Manager)的__init__分析
1. console_driver = FLAGS.console_driver
FLAGS.console_driver定义在该模块中,为nova.console.xvp.XVPConsoleProxy
2. self.driver = utils.import_object(console_driver)
调用nova.utils模块import_object()函数。载入nova.console.xvp.XVPConsoleProxy模块。
当调用import_object(import_str)时候,
import_str=’ nova.console.xvp.XVPConsoleProxy’,所以当该函数执行:
try:
__import__(import_str)
return sys.modules[import_str]
失败, 从而执行
cls = import_class(import_str)
return cls()
即载入模块类,返回类实例
4. xvp.XVPConsoleProxy.__init__()
a) self.xvpconf_template = open(FLAGS.console_xvp_conf_template).read()
b) self.host = FLAGS.host # default, set by manager.
c) super(XVPConsoleProxy, self).__init__()
打开并读取xvp模板,设置设置host参数。
5. super(ConsoleProxyManager, self).__init__(*args, **kwargs)
6.
调用父类的__init__函数,该类的父类为nova.Manager,需要了解函数中*args, **kwargs的用法。
6 nova. Manager.__init__(),。
a. self.host = host
设置基类的host属性,子类也继承该属性
b. super(Manager, self).__init__(db_driver), 该父类继承nova.db.Base()类
调用nova.db.Base()类的._init__函数
7. nova.db.Base._init__()
a. db_driver = FLAGS.db_driver
#db_driver=nova.db.api
self.db = utils.import_object(db_driver)
载入nova.db.api驱动模块,该模块为访问数据库的访问接口。
b. 该模块中的函数都通过IMPL实例来访问数据库。IMPL为utils.LazyPluggable()的实例,LazyPluggable重新实现了__getattr__()函数,即当调用IMPL.XXX()函数时候,即调用的__getattr__()函数中对应的函数。(即IMPL载入nova.db.sqlalchemy.api,并返回其中的API函数)。未深入分析该API接口。
4 RPC模块调用分析
4.1 __init__.py
1. create_connection(new=True)
调用get_impl()函数导入FLAGS.rpc_backend(nova.rpc.impl_kombu)模块,并调用该模块的nova.rpc.impl_kombu.create_connection(true)函数。见4.2
4.2 impl_kombu. Connection. create_connection
1. def create_connection(new=True)
调用该模块的ConnectionContext()函数。 * not x: if x is false, then True, else False 2. ConnectionContext.__init__() 判断pooled,如果为真从连接池中获取一个连接,否则调用Connection()建立一个连接,同时设置polled为true。Connection()为该模块中的类, 3.Connection().__init__() 初始化参数,并调用self.reconnect()建立连接。 由于FLAGS.fake_rabbit=false,所以self.memory_transport=false self.params=dict(hostname='localhost',port=5672, userid= ‘guest’, password=‘guest’, virtual_host='/') 4. reconnect(self) a. if self.connection: try: self.connection.close() except self.connection.connection_errors: pass time.sleep(1) 判断是否已经建立连接,如果是则关闭链接。 c. self.connection = kombu.connection.BrokerConnection(**self.params) 利用kombu.connection.建立连接,其中参数为上面3中设置的参数 d. self.consumer_num = itertools.count(1),数字迭代,从1开始 e. self.connection.ensure_connection()确认链接成功 f. self.channel = self.connection.channel(),从链接中获取一个通道 g. consumer.reconnect(self.channel),将消费者和通道绑定,此时还未有consumer,所以未绑定。
4.3 impl_kombu. Connection. create_consumer
1. create_consumer(self, topic, proxy, fanout=False)
根据fanout类型声明两个消费者,proxy= console创建的Service实例,根据proxy声明ProxyCallback类实例。该类实例实现了对消息的处理,详见6.1
2. declare_consumer(self, consumer_cls, topic, callback)
其中consumer_cls为TopicConsumer或FanoutConsumer。
consumer_cls(self.channel, topic, callback,self.consumer_num.next())
调用类的__init__实例化
self.consumers.append(consumer),将实例化的consumer添加到列表consumers中
3. TopicConsumer.__init__()(类似于FanoutConsumer__init_),该类继承于ConsumerBase
a. kombu.entity.Exchange(http://packages.python.org/kombu/)
利用该函数创建一个Exchange实例。
b. 将参数传递给父类的__init__函数。
4. ConsumerBase.__init__()
该函数初始化参数,并调用reconnet函数。
5. ConsumerBase.reconnet()
该函数利用参数并调用kombu.entity.Queue创建一个消费队列并声明。
4.4 impl_kombu. Connection. consume_in_thread(self)
1 . self.consumer_thread = eventlet.spawn(_consumer_thread)
在一个协程来执行_consumer_thread函数,_consumer_thread函数调用consume(self, limit=None),该函数调用iterconsume(self, limit=None).
iterconsume(self, limit=None) 为一个生成器,利用它生成一个迭代器。然后利用死循环执行迭代器。该处的死循环用来执行it.next().
2. def iterconsume(self, limit=None):
该函数为一个迭代器的生成器,一次迭代调用queue.consume()处理一个产生的事件,该函数定义在该模块的ConsumerBase类中。该处的死循环用来一次迭代完成后继续从头处理事件。
3. ConsumerBase.consume()
该函数初始化事件的回调函数和options字典信息,然后调用利用kombu.entity.Queue(**self.kwargs)创建的队列的函数,
self.queue.consume(*args, callback=_callback, **options),该函数调用6.1的回调函数对事件进行处理,其官方文档说明为: consume(consumer_tag='', callback=None, no_ack=None, nowait=False)¶ Start a queue consumer.Consumers last as long as the channel they were created on, or until the client cancels them.
Parameters:
 consumer_tag – Unique identifier for the consumer. The consumer tag is local to a connection, so two clients can use the same consumer tags. If this field is empty the server will generate a unique tag.
 no_ack – If set messages received does not have to be acknowledged.
 nowait – Do not wait for a reply.
 callback – callback called for each delivered message
5. nova.utils. LoopingCall
5.1 __init__()
初始化参数,其中f分别为Service. report_state()函数和Service. periodic_tasks()函数。
5.2 start(self, interval, now=True)
1. done = event.Event()
调用eventlet.event.Event()函数
2 . self.f(*self.args, **self.kw)
调用Service. report_state()或Service. periodic_tasks()函数
6. nova.rpc.impl_kombu.ProxyCallback
6.1 __call__(self, message_data)
当对消息进行处理时候,调用该函数,
1. ctxt = _unpack_context(message_data)
获取消息的上下文信息,该函数逐级调用顺序为:
_unpack_context()->RpcContext.from_dict()->nova. RequestContext.from_dict()->nova. RequestContext.__init()
2. method = message_data.get('method')
args = message_data.get('args', {})
self.pool.spawn_n(self._process_data, ctxt, method, args)
获取消息的方法和参数信息,然后在协程中调用该类的process_data函数对消息进程处理。
6.2 def _process_data(self, ctxt, method, args)
1. node_func = getattr(self.proxy, str(method))
该函数获取实例的方法函数。在本案中,proxy为console-创建的server实例,即调用nova.Service. __getattr__(self, key)函数,其中key即str(method)。
__getattr__(self, key)调用self.__dict__.get('manager', None)函数,该函数获取该服务的manager,本案中manager为“nova.console.manager.ConsoleProxyManager”实例,即调用“nova.console.manager.ConsoleProxyManager”的__getattr__()函数获取key函数。
总结来说对于其他服务都是调用其服务对应的manager的key对应的函数。
2. node_args = dict((str(k), v) for k, v in args.iteritems())
获取函数所需要的参数信息
3. rval = node_func(context=ctxt, **node_args)
调用函数处理信息
4. ctxt.reply(rval, None),对于rpc.call()类型的信息,返回相关信息。
函数调用顺序:
a. nova.rpc.impl_kombu.msg_reply(msg_id, reply=None, failure=None)
该函数调用conn.direct_send(msg_id, msg),其中conn为ConnectionContext类,由于其重写了__getattr__()函数,所以conn.direct_send(msg_id, msg)会调用Connection. direct_send(msg_id, msg)
b direct_send(self, msg_id, msg)
该函数位于该模块的Connection类中,
该函数调用该类的self.publisher_send(DirectPublisher, msg_id, msg)函数。
C publisher_send(self, cls, topic, msg)
该函数位于该模块的Connection类中
该函数调用cls类,并实例化,并调用该实例的send(msg)函数发送信息。本案汇总cls为DirectPublisher。
5. DirectPublisher(Publisher)
该类继承于Publisher,初始化参数后调用基类的__init__函数继承初始化,并利用参数生成一个exchange,利用该exchange实例一个producer,并调用producer. Publish发送消息。代码如下:
"""Re-establish the Producer after a rabbit reconnection"""
self.exchange = kombu.entity.Exchange(name=self.exchange_name,
**self.kwargs)
self.producer = kombu.messaging.Producer(exchange=self.exchange,
channel=channel, routing_key=self.routing_key)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值