上次分析到了函数_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 = wsgi.Server("Neutron")
#server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
# workers=_get_api_workers())
#下面是server.start函数
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)
self._get_socket函数代码如下
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
该函数调用eventlet库,设置监听端口设置监听端口的属性等。start中调用的另一个重要的函数是_lauch函数,self._launch函数在下面列出来
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)
主意加粗标红的部分。函数WorkerService也是在neutron.wsgi中实现的,看下这个类是如何实现的class 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
一鼓作气,在看下类worker.NeutronWorkerclass 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)
先分析到这里