Neutron-server的启动流程和工作方式(一)

导读:

  • 启动机制
  • Entry point
  • 和Eventlet的交互
  • server的启动过程源代码分析
  • wsgi app详细分析

阅读本文前首先要明白neutron-server是什么,作用是什么?

neutron-server是neutron的核心组件之一,负责直接接受外部请求(包括CLI API,REST API等),然后调度后端相应的plugin进行处理。

启动机制

neutron-server的本质是一个Python Web Server Gateway Interface(WSGI),是通过eventlet lib来实现服务的异步并发模型的。实际工作时,通过'serve_wsgi'启动点(Entry point)来构造了一个NeutronApiService实例,通过该实例生成Eventlet,Greenpool来运行WSGI app并回应客户端请求。
研究之前,首先需要先了解下Entry point的概念。

Entry point

neutron的所有服务(services)的启动点都定义在setup.cfg的"console_scripts"小节中,这些启动点直接指向各个服务的main()函数,这些函数实际位于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#重点关注下这个main函数
 ...

和Eventlet的交互

如果一个service使用eventlet lib,服务本身不用直接调用eventlet.monkey_patch()函数,只需要把相应main()函数放到neutron/cmd/eventlet/...目录下。这样,Eventlet lib启动时,会自动启动各个服务。

server的启动过程源代码分析

通过前面一小节的分析,下面重点关注neutron.cmd.eventlet.server:main()函数:

 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函数中主要是调用了wsgi_eventlet.eventlet_wsgi_server()函数:

 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() 

这个函数通过serve_wsgi()启动了一个wsgi服务,该服务的启动分析(主要是启动neutron.api.v2.base的Controller)下一节详细介绍。同时创建一个GreenPool,从线程池中生成一个api_thread用来监听api命令,当命令到达时,通过wsgi服务路由到neutron.api.v2.base中的Controller中去处理;
另外还生成一个或多个plugin_workers_thread,这个线程和rpc works关联,并监听topics中的消息队列的请求,完成neutron内部组件之间的通信。

wsgi app详细分析

先来看看serve_wsgi()函数:

 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

该函数首先通过函数入参创建了一个service,并调用start方法启动该服务。我们接下来重点关注下函数的入参类,即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     :_listen: The address on which to listen
 54     :_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方法中调用_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

通过调用load_paste_app()函数生成app,并调用run_wsgi_app()函数来启动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

从注释可以了解到,该函数从配置文件构造并返回一个WSGI app。通过跟踪,该配置文件主要是/usr/share/neutron/api-paste.ini, server通过解析该文件来进行指定WSGI app的实现。 在方法load_app中,调用paste的模块库deploy来实现对api_paste.ini中配置信息的解析和app的实现。

【注】:PasteDeployment是一种机制或者说是一种设计模式,它用于在应用WSGI Application和Server提供一个联系的桥梁,并且为用户提供一个接口,当配置好PasteDeployment之后,用户只需调用loadapp方法就可以使用现有的WSGI Application,而保持了WSGIApplication对用户的透明性。

先了解下该文件的内容及格式:

  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

这个文件主要是定义了WSGI app的实现以及路由方式。最终调用了neutronapiapp_v2_0的实现,具体就是neutron.api.v2.router:APIRouter.factory,通过实例化APIRouter类实现app功能的扩展和加载过程,该实例会将Neutorn资源(例如Ports,Networks,Subnets)和URL、controller来进行一一映射。
关于APIRouter部分的实现下一节在继续展开研究。

你可能感兴趣的:(Neutron-server的启动流程和工作方式(一))