服务进程启动如图
调用cinder/cmd/volume.py 的main方法启动服务
def main():
objects.register_all() //导入cinder 的相关orm object
gmr_opts.set_defaults(CONF)
CONF(sys.argv[1:], project='cinder',
version=version.version_string())
logging.setup(CONF, "cinder")
python_logging.captureWarnings(True)
priv_context.init(root_helper=shlex.split(utils.get_root_helper()))
utils.monkey_patch() //打猴子补丁
gmr.TextGuruMeditation.setup_autorun(version, conf=CONF) //捕获异常日志相关
global LOG
LOG = logging.getLogger(__name__) //定义日志
if not CONF.enabled_backends:
LOG.error('Configuration for cinder-volume does not specify '
'"enabled_backends". Using DEFAULT section to configure '
'drivers is not supported since Ocata.')
sys.exit(1)
if os.name == 'nt':
# We cannot use oslo.service to spawn multiple services on Windows.
# It relies on forking, which is not available on Windows.
# Furthermore, service objects are unmarshallable objects that are
# passed to subprocesses.
_launch_services_win32()
else:
_launch_services_posix() //启动Linux服务
实例化launcher,Linux下实例化 ProcessLauncher
def _launch_services_posix():
launcher = service.get_launcher() //调用oslo_service
for backend in filter(None, CONF.enabled_backends):
_launch_service(launcher, backend)
_ensure_service_started()
launcher.wait()
class ProcessLauncher(object):
"""Launch a service with a given number of workers."""
................
调用_launch_service, 实例化server,创建cinder-volume的backend的service记录,并初始化sqlalchemy 数据库连接池,启动服务
def _launch_service(launcher, backend):
CONF.register_opt(host_opt, group=backend)
backend_host = getattr(CONF, backend).backend_host
host = "%s@%s" % (backend_host or CONF.host, backend)
# We also want to set cluster to None on empty strings, and we
# ignore leading and trailing spaces.
cluster = CONF.cluster and CONF.cluster.strip()
cluster = (cluster or None) and '%s@%s' % (cluster, backend)
try:
server = service.Service.create(host=host,
service_name=backend,
binary=constants.VOLUME_BINARY,
coordination=True,
cluster=cluster)
except Exception:
LOG.exception('Volume service %s failed to start.', host)
else:
# Dispose of the whole DB connection pool here before
# starting another process. Otherwise we run into cases where
# child processes share DB connections which results in errors.
session.dispose_engine()
launcher.launch_service(server)
_notify_service_started()
接着调用 ProcessLauncher实例的 launch_service 函数,传入cinder-volume的service,fork出子进程,默认workers=1,即每个backend service默认只启动一个worker干活
def launch_service(self, service, workers=1):
"""Launch a service with a given number of workers.
:param service: a service to launch, must be an instance of
:class:`oslo_service.service.ServiceBase`
:param workers: a number of processes in which a service
will be running
"""
_check_service_base(service)
wrap = ServiceWrapper(service, workers)
# Hide existing objects from the garbage collector, so that most
# existing pages will remain in shared memory rather than being
# duplicated between subprocesses in the GC mark-and-sweep. (Requires
# Python 3.7 or later.)
if hasattr(gc, 'freeze'):
gc.freeze()
LOG.info('Starting %d workers', wrap.workers)
while self.running and len(wrap.children) < wrap.workers:
self._start_child(wrap)
通过os.fork 出子进程,让子进程初始化eventlet协程池,子进程调用add方法,执行run_service, 最终执行cinder-volume的service.start(),此时如果数据库中没有cinder-volume service,则会自动创建service记录。
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('Forking too fast, sleeping')
time.sleep(1)
wrap.forktimes.pop(0)
wrap.forktimes.append(time.time())
pid = os.fork()
if pid == 0:
self.launcher = self._child_process(wrap.service)
while True:
self._child_process_handle_signal()
status, signo = self._child_wait_for_exit_or_signal(
self.launcher)
if not _is_sighup_and_daemon(signo):
self.launcher.wait()
break
self.launcher.restart()
os._exit(status)
LOG.debug('Started child %d', pid)
wrap.children.add(pid)
self.children[pid] = wrap
return pid
至此 cinder-volume 服务启动完成!