您的位置:首页 > 编程语言

nova-api代码分析(3)

2014-08-13 16:01 253 查看
nova-api代码分析,我们继续分析WSGIService的代码。

class WSGIService(object):
"""Provides ability to launch API from a 'paste' configuration."""
#WSGIService的初始化方法
def __init__(self, name, loader=None, use_ssl=False, max_url_len
4000
=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
self.manager = self._get_manager()
#初始化WSGI loader实例
 self.loader = loader or wsgi.Loader()
#调用load_app方法加载paste配置文件
 self.app = self.loader.load_app(name)
#获取主机名
        self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
#获取端口号
self.port = getattr(CONF, '%s_listen_port' % name, 0)
#获取worker进程数
self.workers = (getattr(CONF, '%s_workers' % name, None) or
processutils.get_worker_count())
if self.workers and self.workers < 1:
worker_name = '%s_workers' % name
msg = (_("%(worker_name)s value of %(workers)s is invalid, "
"must be greater than 0") %
{'worker_name': worker_name,
'workers': str(self.workers)})
raise exception.InvalidInput(msg)
self.use_ssl = use_ssl
        #初始化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


wsgi.Server用来初始化WSGI Server

class Server(object):
"""Server class to manage a WSGI server, serving a WSGI application."""
#设定默认池大小,初始化为1000
default_pool_size = CONF.wsgi_default_pool_size

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):
"""Initialize, but do not start, a WSGI server.

:param name: Pretty name for logging.
:param app: The WSGI application to serve.
:param host: IP address to serve the application.
:param port: Port number to server the application.
:param pool_size: Maximum number of eventlets to spawn concurrently.
:param backlog: Maximum number of queued connections.
:param max_url_len: Maximum length of permitted URLs.
:returns: None
:raises: nova.exception.InvalidInput
"""
# Allow operators to customize http requests max header line size.
eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line
self.name = name
self.app = app
self._server = None
self._protocol = protocol
self.pool_size = pool_size or self.default_pool_size
#定义eventlet绿色线程池,默认大小1000线程
self._pool = eventlet.GreenPool(self.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:
#返回socket连接信息信息字典<tt class="docutils literal"><span class="pre">(family,</span> <span class="pre">socktype,</span> <span class="pre">proto,</span> <span class="pre">canonname,</span> <span class="pre">sockaddr)</span></tt>
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

try:
           #打开协程eventlet.listen监听端口
self._socket = eventlet.listen(bind_addr, family, backlog=backlog)
except EnvironmentError:
LOG.error(_("Could not bind to %(host)s:%(port)s"),
{'host': host, 'port': port})
raise
#获取主机和端口信息
(self.host, self.port) = self._socket.getsockname()[0:2]
LOG.info(_("%(name)s listening on %(host)s:%(port)s") % self.__dict__)

到此server = service.WSGIService(api, use_ssl=should_use_ssl)启动WSGIService已经完成,

下面继续分析launcher.launch_service(server, workers=server.workers or 1)

def launch_service(self, service, workers=1):
#service wrapper包装类
wrap = ServiceWrapper(service, workers)

LOG.info(_LI('Starting %d workers'), wrap.workers)
while self.running and len(wrap.children) < wrap.workers:
#启动子进程
self._start_child(wrap)

class ServiceWrapper(object):
def __init__(self, service, workers):
#WSGI Service
self.service = service
#workers工作进程
self.workers = workers
#子进程
self.children = set()
#记录fork进程的时间
self.forktimes = []

def _start_child(self, wrap):
if len(wrap.forktimes) > wrap.workers:
# Limit ourselves to one process a second (over the period of
# number of workers * 1 second). This will allow workers to
# start up quickly but ensure we don't fork off children that
# die instantly too quickly.
if time.time() - wrap.forktimes[0] < wrap.workers:
LOG.info(_LI('Forking too fast, sleeping'))
time.sleep(1)

wrap.forktimes.pop(0)
#记录fork进程时间
wrap.forktimes.append(time.time())
#fork进程
pid = os.fork()
#pid等于0进入到子进程里
if pid == 0:

launcher = self._child_process(wrap.service)
while True:
self._child_process_handle_signal()
status, signo = self._child_wait_for_exit_or_signal(launcher)
if not _is_sighup_and_daemon(signo):
break
launcher.restart()

os._exit(status)

LOG.info(_LI('Started child %d'), pid)

wrap.children.add(pid)
self.children[pid] = wrap

return pid

执行launcher.wait()方法:

def wait(self):
"""Loop waiting on children to die and respawning as necessary."""

LOG.debug('Full set of CONF:')
CONF.log_opt_values(LOG, std_logging.DEBUG)

try:
while True:
self.handle_signal()
self._respawn_children()
if self.sigcaught:
signame = _signo_to_signame(self.sigcaught)
LOG.info(_LI('Caught %s, stopping children'), signame)
if not _is_sighup_and_daemon(self.sigcaught):
break

for pid in self.children:
os.kill(pid, signal.SIGHUP)
self.running = True
self.sigcaught = None
except eventlet.greenlet.GreenletExit:
LOG.info(_LI("Wait called after thread killed.  Cleaning up."))

for pid in self.children:
try:
os.kill(pid, signal.SIGTERM)
except OSError as exc:
if exc.errno != errno.ESRCH:
raise

# Wait for children to die
if self.children:
LOG.info(_LI('Waiting on %d children to exit'), len(self.children))
while self.children:
self._wait_child()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: