上次分析到了函数_eventlet_wsgi_server,函数在neutron/server/wsgi_eventlet.py中
def _eventlet_wsgi_server():
pool = eventlet.GreenPool()
neutron_api = service.serve_wsgi(service.NeutronApiService)
api_thread = pool.spawn(neutron_api.wait)
try:
neutron_rpc = service.serve_rpc()
except NotImplementedError:
LOG.info(_LI("RPC was already started in parent process by "
"plugin."))
else:
rpc_thread = pool.spawn(neutron_rpc.wait)
plugin_workers = service.start_plugin_workers()
for worker in plugin_workers:
pool.spawn(worker.wait)
# api and rpc should die together. When one dies, kill the other.
rpc_thread.link(lambda gt: api_thread.kill())
api_thread.link(lambda gt: rpc_thread.kill())
pool.waitall()
该函数中需要特别关注的是两个函数service.serve_wsgi和service.serve_rpc,这两个函数分别完成了wsgi和rpc的初始化。
先看下server.serve_wsgi函数,该函数是在neutron.server.py中实现的,代码如下
class NeutronApiService(WsgiService):
"""Class for neutron-api service."""
@classmethod
def create(cls, app_name='neutron'):
# Setup logging early, supplying both the CLI options and the
# configuration mapping from the config file
# We only update the conf dict for the verbose and debug
# flags. Everything else must be set up in the conf file...
# Log the options used when starting if we're in debug mode...
config.setup_logging()
service = cls(app_name)
return service
def serve_wsgi(cls):
try:
service = cls.create()
service.start()
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unrecoverable error: please check log '
'for details.'))
return service
从调用关系上看serve_wsgi()中的cls就是NeutronApiService。在上面的代码中也列出了类NeutronApiService的实现情况。
在上面的代码中我们看到,NeutronApiService继承了类WsgiService,而且还增加了一个create方法。在create方法中,设置了logging,也就是日志的配置,然后就调用了
WsgiService的构造函数,讲app_name作为参数传入到了WsgiService的构造函数中,通过上下文代码我们看到,app_name的名字为"Neutron"。下面看下WsgiService类的实现。
class WsgiService(object):
"""Base class for WSGI based services. For each api you define, you must also define these flags: :<api>_listen: The address on which to listen :<api>_listen_port: The port on which to listen """ def __init__(self, app_name): self.app_name = app_name self.wsgi_app = None def start(self): self.wsgi_app = _run_wsgi(self.app_name) def wait(self): self.wsgi_app.wait()#注意加粗的函数_run_wsgi,继续跟踪这个函数
def _run_wsgi(app_name): app = config.load_paste_app(app_name)//这是从配置文件/usr/share/neutron/api-paste.ini加载的,后面会单独介绍加载 if not app: LOG.error(_LE('No known API applications configured.')) return server = wsgi.Server("Neutron") server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host, workers=_get_api_workers()) LOG.info(_LI("Neutron service started, listening on %(host)s:%(port)s"), {'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port}) return server
#这个函数有完成几处功能,第一
#app = config.load_paste_app(app_name)#加载配置文件/usr/share/neutron/api-paste.ini#第二是启动wsgi server,看下代码
#下面是server.start函数#server = wsgi.Server("Neutron") #server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host, # workers=_get_api_workers())
self._get_socket函数代码如下class Server(object): """Server class to manage multiple WSGI sockets and applications.""" def __init__(self, name, num_threads=1000, disable_ssl=False): # Raise the default from 8192 to accommodate large tokens eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line self.num_threads = num_threads self.disable_ssl = disable_ssl # Pool for a greenthread in which wsgi server will be running ... ..... def start(self, application, port, host='0.0.0.0', workers=0): """Run a WSGI server with the given application.""" self._host = host self._port = port backlog = CONF.backlog self._socket = self._get_socket(self._host, self._port, backlog=backlog) self._launch(application, workers)
该函数调用eventlet库,设置监听端口设置监听端口的属性等。def _get_socket(self, host, port, backlog): bind_addr = (host, port) # TODO(dims): eventlet's green dns/socket module does not actually # support IPv6 in getaddrinfo(). We need to get around this in the # future or monitor upstream for a fix try: info = socket.getaddrinfo(bind_addr[0], bind_addr[1], socket.AF_UNSPEC, socket.SOCK_STREAM)[0] family = info[0] bind_addr = info[-1] except Exception: LOG.exception(_LE("Unable to listen on %(host)s:%(port)s"), {'host': host, 'port': port}) sys.exit(1) sock = None retry_until = time.time() + CONF.retry_until_window while not sock and time.time() < retry_until: try: sock = eventlet.listen(bind_addr, backlog=backlog, family=family) except socket.error as err: with excutils.save_and_reraise_exception() as ctxt: if err.errno == errno.EADDRINUSE: ctxt.reraise = False eventlet.sleep(0.1) if not sock: raise RuntimeError(_("Could not bind to %(host)s:%(port)s " "after trying for %(time)d seconds") % {'host': host, 'port': port, 'time': CONF.retry_until_window}) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # sockets can hang around forever without keepalive sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # This option isn't available in the OS X version of eventlet if hasattr(socket, 'TCP_KEEPIDLE'): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, CONF.tcp_keepidle) return sock
start中调用的另一个重要的函数是_lauch函数,self._launch函数在下面列出来
主意加粗标红的部分。函数WorkerService也是在neutron.wsgi中实现的,看下这个类是如何实现的def _launch(self, application, workers=0): #application是前面传来的app,也就是从文件/usr/share/neutron/api-paste.ini #加载的一些内容 service = WorkerService(self, application, self.disable_ssl) if workers < 1: # The API service should run in the current process. self._server = service # Dump the initial option values cfg.CONF.log_opt_values(LOG, logging.DEBUG) service.start() systemd.notify_once() else: # dispose the whole pool before os.fork, otherwise there will # be shared DB connections in child processes which may cause # DB errors. api.dispose() # The API service runs in a number of child processes. # Minimize the cost of checking for child exit by extending the # wait interval past the default of 0.01s. self._server = common_service.ProcessLauncher(cfg.CONF, wait_interval=1.0) self._server.launch_service(service, workers=workers)
一鼓作气,在看下类worker.NeutronWorkerclass WorkerService(worker.NeutronWorker): """Wraps a worker to be handled by ProcessLauncher""" def __init__(self, service, application, disable_ssl=False): self._service = service self._application = application self._disable_ssl = disable_ssl self._server = None def start(self): super(WorkerService, self).start() # When api worker is stopped it kills the eventlet wsgi server which # internally closes the wsgi server socket object. This server socket # object becomes not usable which leads to "Bad file descriptor" # errors on service restart. # Duplicate a socket object to keep a file descriptor usable. dup_sock = self._service._socket.dup() if CONF.use_ssl and not self._disable_ssl: dup_sock = self._service.wrap_ssl(dup_sock) self._server = self._service.pool.spawn(self._service._run, self._application, dup_sock) def wait(self): if isinstance(self._server, eventlet.greenthread.GreenThread): self._server.wait() def stop(self): if isinstance(self._server, eventlet.greenthread.GreenThread): self._server.kill() self._server = None
先分析到这里class NeutronWorker(service.ServiceBase): """Partial implementation of the ServiceBase ABC Subclasses will still need to add the other abstractmethods defined in service.ServiceBase. See oslo_service for more details. If a plugin needs to handle synchornization with the Neutron database and do this only once instead of in every API worker, for instance, it would define a NeutronWorker class and the plugin would have get_workers return an array of NeutronWorker instnaces. For example: class MyPlugin(...): def get_workers(self): return [MyPluginWorker()] class MyPluginWorker(NeutronWorker): def start(self): super(MyPluginWorker, self).start() do_sync() """ def start(self): registry.notify(resources.PROCESS, events.AFTER_CREATE, self.start)