# nova.cmd.api:main
def main():
config.parse_args(sys.argv)
logging.setup(CONF, "nova")
utils.monkey_patch()
objects.register_all()
log = logging.getLogger(__name__)
gmr.TextGuruMeditation.setup_autorun(version)
launcher = service.process_launcher()
started = 0
for api in CONF.enabled_apis: # 默认是'osapi_compute', 'metadata'
should_use_ssl = api in CONF.enabled_ssl_apis
try:
# 初始化一个nova.services.WSGIService类对象
server = service.WSGIService(api, use_ssl=should_use_ssl)
# 启动wsgi服务
launcher.launch_service(server, workers=server.workers or 1)
started += 1
except exception.PasteAppNotFound as ex:
log.warning(
_LW("%s. ``enabled_apis`` includes bad values. "
"Fix to remove this warning."), six.text_type(ex))
if started == 0:
log.error(_LE('No APIs were started. '
'Check the enabled_apis config option.'))
sys.exit(1)
launcher.wait()
上述代码主要做了两件事情,初始化一个nova.services.WSGIServiced的类对象,然后启动它。注意到对于每一个可能的api,有对应的启动脚本,如osapi_compute,其对应的启动脚本为nova.cmd.api_osapi_compute.py,代码都是类似的。主要看下WSGIService的初始化过程。
# nova.services.WSGISerivce
class WSGIService(object):
"""Provides ability to launch API from a 'paste' configuration."""
def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
"""Initialize, but do not start the WSGI server.
:param name: The name of the WSGI server given to the loader.
:param loader: Loads the WSGI application using the given name.
:returns: None
"""
self.name = name # 'osapi_compute', 'metadata', 'ec2'中的一个
self.manager = self._get_manager() # 加载一个子类,默认为空
self.loader = loader or wsgi.Loader() # 封装下paste.deploy的loadapp方法
self.app = self.loader.load_app(name) # 加载指定的APP
self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0") # host ip地址
self.port = getattr(CONF, '%s_listen_port' % name, 0) # 监听的端口
self.workers = getattr(CONF, '%s_workers' % name, None) # api-worker的数量
self.use_ssl = use_ssl
# 初始化一个nova.wsgi.server类对象
self.server = wsgi.Server(name,
self.app,
host=self.host,
port=self.port,
use_ssl=self.use_ssl,
max_url_len=max_url_len)
# Pull back actual port used
self.port = self.server.port
self.backdoor_port = None
nova.wsgi.server
class Server(object):
"""Server class to manage a WSGI server, serving a WSGI application."""
default_pool_size = 1000
def __init__(self, name, app, host='0.0.0.0', port=0, pool_size=None,
protocol=eventlet.wsgi.HttpProtocol, backlog=128,
use_ssl=False, max_url_len=None):
self.name = name
self.app = app
self._server = None
self._protocol = protocol
# 初始化一个默认大小为1000的绿色线程池
self._pool = eventlet.GreenPool(pool_size or self.default_pool_size)
self._logger = logging.getLogger("nova.%s.wsgi.server" % self.name)
self._wsgi_logger = logging.WritableLogger(self._logger)
self._use_ssl = use_ssl
self._max_url_len = max_url_len
if backlog < 1:
raise exception.InvalidInput(
reason='The backlog must be more than 1')
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:
family = socket.AF_INET
# 建立socket,监听host:port
self._socket = eventlet.listen(bind_addr, family, backlog=backlog)
(self.host, self.port) = self._socket.getsockname()[0:2]
LOG.info(_("%(name)s listening on %(host)s:%(port)s") % self.__dict__)
def start(self):
wsgi_kwargs = {
'func': eventlet.wsgi.server,
'sock': self._socket,
'site': self.app,
'protocol': self._protocol,
'custom_pool': self._pool,
'log': self._wsgi_logger,
'log_format': CONF.wsgi_log_format
}
self._server = eventlet.spawn(**wsgi_kwargs)
while True:
try:
client_socket = sock.accept()
client_socket[0].settimeout(serv.socket_timeout)
if debug:
serv.log.write("(%s) accepted %r\n" % (
serv.pid, client_socket[1]))
try:
pool.spawn_n(serv.process_request, client_socket)
except AttributeError:
···
这里就是循环的调用sock.accept() 接收请求,每次接收到一个http的请求,就调用 pool.spawn_n() 启动一个协程处理该请求,由此来实现并发请求处理。
# nova.wsgi.Loader:load_app
def load_app(self, name):
try:
LOG.debug(_("Loading app %(name)s from %(path)s") %
{'name': name, 'path': self.config_path})
return deploy.loadapp("config:%s" % self.config_path, name=name)
except LookupError as err:
LOG.error(err)
raise exception.PasteAppNotFound(name=name, path=self.config_path)
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v2
/v2: openstack_compute_api_v2
/v3: openstack_compute_api_v3
[composite:openstack_compute_api_v2]
keystone = statsd faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
[filter:statsd]
paste.filter_factory = nova.api.openstack.compute.stats_notifier:filter_factory
[filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
[filter:ratelimit]
paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory
[filter:sizelimit]
paste.filter_factory = nova.api.sizelimit:RequestBodySizeLimiter.factory
[app:osapi_compute_app_v2]
paste.app_factory = nova.api.openstack.compute:APIRouter.factory
[filter:keystonecontext]
paste.filter_factory = nova.api.auth:NovaKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
# nova.api.openstack.APIRouter:fatory
@classmethod
def factory(cls, global_config, **local_config):
"""Simple paste factory, :class:`nova.wsgi.Router` doesn't have one."""
return cls()
def __init__(self, ext_mgr=None, init_only=None):
if ext_mgr is None:
if self.ExtensionManager:
ext_mgr = self.ExtensionManager()
else:
raise Exception(_("Must specify an ExtensionManager class"))
mapper = ProjectMapper()
self.resources = {}
self._setup_routes(mapper, ext_mgr, init_only) # 加载核心资源
self._setup_ext_routes(mapper, ext_mgr, init_only) # 加载扩展资源
self._setup_extensions(ext_mgr) # 加载扩展控制类
super(APIRouter, self).__init__(mapper)
其中self._setup_routes的具体实现在nova.api.openstack.compute:APIRouter中,主要是加载了一些核心资源,包括创建、删除云主机、获取云主机列表等,这里只列了云主机相关的资源。
if init_only is None or 'consoles' in init_only or \
'servers' in init_only or ips in init_only:
self.resources['servers'] = servers.create_resource(ext_mgr)
mapper.resource("server", "servers",
controller=self.resources['servers'],
collection={'detail': 'GET'},
member={'action': 'POST'})
# nova.api.openstack.compute.servers.py
def create_resource(ext_mgr):
return wsgi.Resource(Controller(ext_mgr))
Controller对象放入wsgi.Resource方法调用,Controller中是本servers.py中最重要的部分,里面有主要的API包括create、detail、show等,看下wsgi.Resource方法。
# api.openstack.compute.wsgi.py:Resource
class Resource(wsgi.Application):
def __init__(self, controller, action_peek=None, inherits=None,
**deserializers):
self.controller = controller
···
self.wsgi_actions = {}
if controller:
self.register_actions(controller)
···
def register_actions(self, controller):
"""Registers controller actions with this resource."""
actions = getattr(controller, 'wsgi_actions', {})
for key, method_name in actions.items():
self.wsgi_actions[key] = getattr(controller, method_name)
# nova/api/openstack/compute/servers.py:Controller.create
class Controller(wsgi.Controller):
@wsgi.response(202)
@wsgi.serializers(xml=FullServerTemplate)
@wsgi.deserializers(xml=CreateDeserializer)
def create(self, req, body):
"""Creates a new server for a given user."""
if not self.is_valid_body(body, 'server'):
raise exc.HTTPUnprocessableEntity()
context = req.environ['nova.context']
# 各种参数处理。。
···
(instances, resv_id) = self.compute_api.create(context,
inst_type,
image_uuid,
display_name=name,
display_description=name,
key_name=key_name,
metadata=server_dict.get('metadata', {}),
····)
本文来自网易实践者社区,经作者廖跃华授权发布。