openstack nova 基礎知識——從源碼看一個服務是如何啟動的


nova中服務分為兩種:Service和WSGIService,后者我還不清楚是用來做什么工作的,但是前者看了一下bin中的代碼,知道了它是scheduler, compute, network等這些組件所使用的服務。

相對這兩種服務,就有兩種加載的方法:ServiceLauncher和ProcessLauncher,看代碼這兩者的主要區別是前者放到了綠色線程中,后者則是用os.fork()出來的進程。

看一下整體的類圖:




下面就以Scheduler服務的啟動為例,來看一下啟動的過程:

    server = service.Service.create(binary='nova-scheduler')
service.serve(server)
service.wait()

1.  第一行是調用Service中的類方法create()創建了一個Service對象,這里僅僅是創建了一個對象,所做的工作是初始化類屬性,比如這里只傳遞了一個binary參數進去,但是一個Service不可能只需要這一個參數,那么其它的參數就是要從配置文件中讀取或是使用默認的值。來看一下create()方法:

    @classmethod
def create(cls, host=None, binary=None, topic=None, manager=None,
report_interval=None, periodic_interval=None,
periodic_fuzzy_delay=None):
"""Instantiates class and passes back application object.

:param host: defaults to FLAGS.host
:param binary: defaults to basename of executable
:param topic: defaults to bin_name - 'nova-' part
:param manager: defaults to FLAGS.<topic>_manager
:param report_interval: defaults to FLAGS.report_interval
:param periodic_interval: defaults to FLAGS.periodic_interval
:param periodic_fuzzy_delay: defaults to FLAGS.periodic_fuzzy_delay

"""
if not host:
host = FLAGS.host #host='ubuntu'
if not binary: #binary='nova-scheduler'
binary = os.path.basename(inspect.stack()[-1][1])
if not topic:
topic = binary.rpartition('nova-')[2] #topic='scheduler'
if not manager: #manager='nova.scheduler.manager.SchedulerManager'
manager = FLAGS.get('%s_manager' % topic, None)
if report_interval is None:
report_interval = FLAGS.report_interval
if periodic_interval is None:
periodic_interval = FLAGS.periodic_interval
if periodic_fuzzy_delay is None:
periodic_fuzzy_delay = FLAGS.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
從配置文件讀取出來其它參數之后,調用cls()來真正的實例化一個類,注意這里create()是一個類方法,傳遞進來的第一個參數cls就是Service類本身。


2.  創建好Service對象之后,就是加載服務了,那就是上面第二行所做的事,Scheduler服務是用ServiceLauncher來加載的:

        _launcher = ServiceLauncher()
_launcher.launch_server(server)
    def launch_server(self, server):        """Load and start the given server.        :param server: The server you would like to start.        :returns: None        """        gt = eventlet.spawn(self.run_server, server)        self._services.append(gt)
    @staticmethod    def run_server(server):        """Start and wait for a server to finish.        :param service: Server to run and wait for.        :returns: None        """        server.start()#尼媽啊,終於找到你了,原來是在這調用的start(),太隱蔽了啊!!!        server.wait()
從launcher_server()中可以看到加載一個服務是放到了綠色線程中,執行這個run_server()方法。run_server()方法執行server.start()和server.wait(),完成了服務的啟動,然后等待,一開始我看到Service類中有個start()方法,知道一個服務的啟動實質性的內容都在這個方法里,但是就是找不到在哪里調用了這個方法,原來是在這里。那么接下來就進到這個start()中了。

3.  start()主要做的工作是從數據庫中取得服務的相關信息,還有創建了三個不同類型的Consumer,用來接收消息,看一下代碼:

    def start(self):
vcs_string = version.version_string_with_vcs()
LOG.audit(_('Starting %(topic)s node (version %(vcs_string)s)'),
{'topic': self.topic, 'vcs_string': vcs_string})
utils.cleanup_file_locks()
self.manager.init_host()
self.model_disconnected = False
ctxt = context.get_admin_context()#返回一個RequestContext對象,並且屬性is_admin=True
try:
# 得到在‘host’主機中的‘binary’的‘service’在數據庫中保存的狀態
service_ref = db.service_get_by_args(ctxt,
self.host,
self.binary)
self.service_id = service_ref['id'] #得到服務的id,因為models.Service有迭代器的性質
except exception.NotFound:
self._create_service_ref(ctxt)

if 'nova-compute' == self.binary:
self.manager.update_available_resource(ctxt)

# 從連接池中取得一個連接
self.conn = rpc.create_connection(new=True)
LOG.debug(_("Creating Consumer connection for Service %s") %
self.topic)

rpc_dispatcher = self.manager.create_rpc_dispatcher()

# Share this same connection for these Consumers
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)

# Consume from all consumers in a thread
self.conn.consume_in_thread()#把consumer放到綠色線程中,並且啟動監聽消息

if self.report_interval:
pulse = utils.LoopingCall(self.report_state)
pulse.start(interval=self.report_interval,
initial_delay=self.report_interval)
self.timers.append(pulse)

if self.periodic_interval:
if self.periodic_fuzzy_delay:
initial_delay = random.randint(0, self.periodic_fuzzy_delay)
else:
initial_delay = None

periodic = utils.LoopingCall(self.periodic_tasks)
periodic.start(interval=self.periodic_interval,
initial_delay=initial_delay)
self.timers.append(periodic)
主要來看一下創建Consumer,創建Consumer其實就是創建Queue,並且聲明topic,告訴Exchange我要接收那些消息,這里定義了三個Consumer,兩個Topic類型的Consumer和一個Fanout類型的Consumer,然后通過consumer_in_thread()把consumer的監聽程序(drain_event())放到了綠色線程中。


這樣一個服務就啟動了,然后調用server.wait()來等待服務結束。。





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com