導(dǎo)讀:
- 啟動(dòng)機(jī)制
- Entry point
- 和Eventlet的交互
- server的啟動(dòng)過(guò)程源代碼分析
- wsgi app詳細(xì)分析
閱讀本文前首先要明白neutron-server是什么,作用是什么?
neutron-server是neutron的核心組件之一,負(fù)責(zé)直接接受外部請(qǐng)求(包括CLI API,REST API等),然后調(diào)度后端相應(yīng)的plugin進(jìn)行處理。
啟動(dòng)機(jī)制
neutron-server的本質(zhì)是一個(gè)Python Web Server Gateway Interface(WSGI),是通過(guò)eventlet lib來(lái)實(shí)現(xiàn)服務(wù)的異步并發(fā)模型的。實(shí)際工作時(shí),通過(guò)'serve_wsgi'啟動(dòng)點(diǎn)(Entry point)來(lái)構(gòu)造了一個(gè)NeutronApiService實(shí)例,通過(guò)該實(shí)例生成Eventlet,Greenpool來(lái)運(yùn)行WSGI app并回應(yīng)客戶端請(qǐng)求。
研究之前,首先需要先了解下Entry point的概念。
Entry point
neutron的所有服務(wù)(services)的啟動(dòng)點(diǎn)都定義在setup.cfg的"console_scripts"小節(jié)中,這些啟動(dòng)點(diǎn)直接指向各個(gè)服務(wù)的main()函數(shù),這些函數(shù)實(shí)際位于neutron/cmd/...目錄下。
代碼如下:
43 [entry_points]
44 console_scripts =
45 neutron-db-manage = neutron.db.migration.cli:main
46 neutron-debug = neutron.debug.shell:main
47 neutron-dhcp-agent = neutron.cmd.eventlet.agents.dhcp:main
48 neutron-keepalived-state-change = neutron.cmd.keepalived_state_change:main
49 neutron-ipset-cleanup = neutron.cmd.ipset_cleanup:main
50 neutron-l3-agent = neutron.cmd.eventlet.agents.l3:main
51 neutron-linuxbridge-agent = neutron.cmd.eventlet.plugins.linuxbridge_neutron_agent:main
52 neutron-linuxbridge-cleanup = neutron.cmd.linuxbridge_cleanup:main
53 neutron-macvtap-agent = neutron.cmd.eventlet.plugins.macvtap_neutron_agent:main
54 neutron-metadata-agent = neutron.cmd.eventlet.agents.metadata:main
55 neutron-netns-cleanup = neutron.cmd.netns_cleanup:main
56 neutron-openvswitch-agent = neutron.cmd.eventlet.plugins.ovs_neutron_agent:main
57 neutron-ovs-cleanup = neutron.cmd.ovs_cleanup:main
58 neutron-pd-notify = neutron.cmd.pd_notify:main
59 neutron-server = neutron.cmd.eventlet.server:main#重點(diǎn)關(guān)注下這個(gè)main函數(shù)
...
和Eventlet的交互
如果一個(gè)service使用eventlet lib,服務(wù)本身不用直接調(diào)用eventlet.monkey_patch()函數(shù),只需要把相應(yīng)main()函數(shù)放到neutron/cmd/eventlet/...目錄下。這樣,Eventlet lib啟動(dòng)時(shí),會(huì)自動(dòng)啟動(dòng)各個(gè)服務(wù)。
server的啟動(dòng)過(guò)程源代碼分析
通過(guò)前面一小節(jié)的分析,下面重點(diǎn)關(guān)注neutron.cmd.eventlet.server:main()函數(shù):
13 from neutron import server
14 from neutron.server import rpc_eventlet
15 from neutron.server import wsgi_eventlet
16
17
18 def main():
19 server.boot_server(wsgi_eventlet.eventlet_wsgi_server)
20
21
22 def main_rpc_eventlet():
23 server.boot_server(rpc_eventlet.eventlet_rpc_server)
main函數(shù)中主要是調(diào)用了wsgi_eventlet.eventlet_wsgi_server()函數(shù):
24 def eventlet_wsgi_server():
25 neutron_api = service.serve_wsgi(service.NeutronApiService)
26 start_api_and_rpc_workers(neutron_api)
27
28
29 def start_api_and_rpc_workers(neutron_api):
30 try:
31 worker_launcher = service.start_all_workers()
32
33 pool = eventlet.GreenPool()
34 api_thread = pool.spawn(neutron_api.wait)
35 plugin_workers_thread = pool.spawn(worker_launcher.wait)
36
37 # api and other workers should die together. When one dies,
38 # kill the other.
39 api_thread.link(lambda gt: plugin_workers_thread.kill())
40 plugin_workers_thread.link(lambda gt: api_thread.kill())
41
42 pool.waitall()
43 except NotImplementedError:
44 LOG.info(_LI("RPC was already started in parent process by "
45 "plugin."))
46
47 neutron_api.wait()
這個(gè)函數(shù)通過(guò)serve_wsgi()啟動(dòng)了一個(gè)wsgi服務(wù),該服務(wù)的啟動(dòng)分析(主要是啟動(dòng)neutron.api.v2.base的Controller)下一節(jié)詳細(xì)介紹。同時(shí)創(chuàng)建一個(gè)GreenPool,從線程池中生成一個(gè)api_thread用來(lái)監(jiān)聽api命令,當(dāng)命令到達(dá)時(shí),通過(guò)wsgi服務(wù)路由到neutron.api.v2.base中的Controller中去處理;
另外還生成一個(gè)或多個(gè)plugin_workers_thread,這個(gè)線程和rpc works關(guān)聯(lián),并監(jiān)聽topics中的消息隊(duì)列的請(qǐng)求,完成neutron內(nèi)部組件之間的通信。
wsgi app詳細(xì)分析
先來(lái)看看serve_wsgi()函數(shù):
83 def serve_wsgi(cls):
84
85 try:
86 service = cls.create()
87 service.start()
88 except Exception:
89 with excutils.save_and_reraise_exception():
90 LOG.exception(_LE('Unrecoverable error: please check log '
91 'for details.'))
92
93 registry.notify(resources.PROCESS, events.BEFORE_SPAWN, service)
94 return service
該函數(shù)首先通過(guò)函數(shù)入?yún)?chuàng)建了一個(gè)service,并調(diào)用start方法啟動(dòng)該服務(wù)。我們接下來(lái)重點(diǎn)關(guān)注下函數(shù)的入?yún)㈩?,即NeutronApiService:
49 class WsgiService(object):
50 """Base class for WSGI based services.
51
52 For each api you define, you must also define these flags:
53 :<api>_listen: The address on which to listen
54 :<api>_listen_port: The port on which to listen
55
56 """
57
58 def __init__(self, app_name):
59 self.app_name = app_name
60 self.wsgi_app = None
61
62 def start(self):
63 self.wsgi_app = _run_wsgi(self.app_name)
64
65 def wait(self):
66 self.wsgi_app.wait()
67
68
69 class NeutronApiService(WsgiService):
70 """Class for neutron-api service."""
71 def __init__(self, app_name):
72 profiler.setup('neutron-server', cfg.CONF.host)
73 super(NeutronApiService, self).__init__(app_name)
74
75 @classmethod
76 def create(cls, app_name='neutron'):
77 # Setup logging early
78 config.setup_logging()
79 service = cls(app_name)
80 return service
在start方法中調(diào)用_run_wsgi():
287 def _run_wsgi(app_name):
288 app = config.load_paste_app(app_name)
289 if not app:
290 LOG.error(_LE('No known API applications configured.'))
291 return
292 return run_wsgi_app(app)
293
294
295 def run_wsgi_app(app):
296 server = wsgi.Server("Neutron")
297 server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
298 workers=_get_api_workers())
299 LOG.info(_LI("Neutron service started, listening on %(host)s:%(port)s"),
300 {'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port})
301 return server
通過(guò)調(diào)用load_paste_app()函數(shù)生成app,并調(diào)用run_wsgi_app()函數(shù)來(lái)啟動(dòng)app:
126 def load_paste_app(app_name):
127 """Builds and returns a WSGI app from a paste config file.
128
129 :param app_name: Name of the application to load
130 """
131 loader = wsgi.Loader(cfg.CONF)
132 app = loader.load_app(app_name)
133 return app
從注釋可以了解到,該函數(shù)從配置文件構(gòu)造并返回一個(gè)WSGI app。通過(guò)跟蹤,該配置文件主要是/usr/share/neutron/api-paste.ini, server通過(guò)解析該文件來(lái)進(jìn)行指定WSGI app的實(shí)現(xiàn)。 在方法load_app中,調(diào)用paste的模塊庫(kù)deploy來(lái)實(shí)現(xiàn)對(duì)api_paste.ini中配置信息的解析和app的實(shí)現(xiàn)。
【注】:PasteDeployment是一種機(jī)制或者說(shuō)是一種設(shè)計(jì)模式,它用于在應(yīng)用WSGI Application和Server提供一個(gè)聯(lián)系的橋梁,并且為用戶提供一個(gè)接口,當(dāng)配置好PasteDeployment之后,用戶只需調(diào)用loadapp方法就可以使用現(xiàn)有的WSGI Application,而保持了WSGIApplication對(duì)用戶的透明性。
先了解下該文件的內(nèi)容及格式:
1 [composite:neutron]
2 use = egg:Paste#urlmap
3 /: neutronversions_composite
4 /v2.0: neutronapi_v2_0
5
6 [composite:neutronapi_v2_0]
7 use = call:neutron.auth:pipeline_factory
8 noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0
9 keystone = cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neut ronapiapp_v2_0
...
32 [filter:authtoken]
33 paste.filter_factory = keystonemiddleware.auth_token:filter_factory
34
35 [filter:extensions]
36 paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory
37
38 [app:neutronversions]
39 paste.app_factory = neutron.api.versions:Versions.factory
40
41 [app:neutronapiapp_v2_0]
42 paste.app_factory = neutron.api.v2.router:APIRouter.factory
這個(gè)文件主要是定義了WSGI app的實(shí)現(xiàn)以及路由方式。最終調(diào)用了neutronapiapp_v2_0的實(shí)現(xiàn),具體就是neutron.api.v2.router:APIRouter.factory,通過(guò)實(shí)例化APIRouter類實(shí)現(xiàn)app功能的擴(kuò)展和加載過(guò)程,該實(shí)例會(huì)將Neutorn資源(例如Ports,Networks,Subnets)和URL、controller來(lái)進(jìn)行一一映射。
關(guān)于APIRouter部分的實(shí)現(xiàn)下一節(jié)在繼續(xù)展開研究。