接着上一次的代码解读,我们已经获得了各项命令,start stop...除此之外还有,根据main获得的各项服务的类的对象(假设输入的是swift-init main start)
接下来要做的就是分析run_command命令,该函数是在manager.py当中的
现在看看self.conf_files()是如何获得配置文件的
接下来看看最主要的函数spawn,该函数进行了进程的启动
try:
#加入用户输入的是swift-init start,那么command则是start 而__dict__则是对象的所有属性
status = manager.run_command(command, **options.__dict__)
except UnknownCommandError:
parser.print_help()
print 'ERROR: unknown command, %s' % command
status = 1
return 1 if status else 0
接下来要做的就是分析run_command命令,该函数是在manager.py当中的
def start(self, **kwargs):
"""starts a server
"""
#
setup_env()
status = 0
#在构造函数中已经根据servers添加了相应的Server类,此时就是main
for server in self.servers:
#服务启动函数
server.launch(**kwargs)
if not kwargs.get('daemon', True):
for server in self.servers:
try:
status += server.interact(**kwargs)
except KeyboardInterrupt:
print _('\nuser quit')
self.stop(**kwargs)
break
elif kwargs.get('wait', True):
for server in self.servers:
status += server.wait(**kwargs)
return status
我们知道,现在主要的就是Server类的成员函数launch函数,继续跟进,看看这个函数做了什么吧....
#服务启动函数
def launch(self, **kwargs):
"""
Collect conf files and attempt to spawn the processes for this server,收集配置文件,尝试为该服务创建子进程
"""
#获得配置文件信息,在SWIFT_DIR中进行查找 服务名称.conf的文件 服务名称文件夹里面所有的*.conf文件
conf_files = self.conf_files(**kwargs)
if not conf_files:
return {}
现在看看self.conf_files()是如何获得配置文件的
def conf_files(self, **kwargs):
"""Get conf files for this server为服务获得配置文件
:param: number, if supplied will only lookup the nth server
:returns: list of conf files,返回配置文件列表
"""
#此时已经使用过构造函数,也就是说self.server的值已经确定了,
#整好用我们事先确定的['proxy-server', 'account-server', 'container-server','object-server'],也即self.conf=None
if self.server in STANDALONE_SERVERS:#STANDALONE_SERVERS = ['object-expirer', 'container-reconciler']此时,server_search没有进行设置
server_search = self.server
else:
server_search = "%s-server" % self.type#这里确定的是['proxy-server', 'account-server', 'container-server','object-server']
#目录名称,启动时server_search='proxy-server'
if self.conf is not None:
found_conf_files = search_tree(SWIFT_DIR, server_search,
self.conf + '.conf',
dir_ext=self.conf + '.conf.d')
else:
#寻找相应的配置文件 SWIFT_DIR = '/etc/swift' server_search="" 文件扩展名 目录扩展名匹配字符是*
#search_tree在SWIFT_DIR中寻找{server_type}-server.conf的文件,或是{server-type}的文件夹
#如果是文件夹则在此文件夹中寻找后缀名是.conf的文件,根据我们之前的部署,应该是获得两个文件swift-proxy.conf和swift.conf文件(因为/etc/swift
#下只有这两个文件符合条件)
found_conf_files = search_tree(SWIFT_DIR, + + '*',
'.conf', dir_ext='.conf.d')
#该值是用户输入的默认是0
number = kwargs.get('number')
if number:
try:
#设定了索引,则只使用该索引的配置文件
conf_files = [found_conf_files[number - 1]]
except IndexError:
conf_files = []
else:
#如果是0则取出,或者没有设定,则使用系统自己找到的
conf_files = found_conf_files
#如果经过一系列查找,并未找到配置文件,则读取用户输入命令
if not conf_files:
# maybe there's a config file(s) out there, but I couldn't find it!
if not kwargs.get('quiet'):
print _('Unable to locate config %sfor %s') % (
('number %s ' % number if number else ''), self.server)
if kwargs.get('verbose') and not kwargs.get('quiet'):
if found_conf_files:
print _('Found configs:')
for i, conf_file in enumerate(found_conf_files):
print ' %d) %s' % (i + 1, conf_file)
return conf_files
这样我们就获得了swift-proxy.con文件的路径,然后根据这个配置文件进行服务的启动(疑问这里self.conf到底是不是None呢??)
根据配置文件继续运行
if not conf_files:
return {}
#向signal_pids 发送signal.SIG_DFL参数,进行调用,返回的是键值对一个是 PID 一个是PIDFILE
pids = self.get_running_pids(**kwargs)
如果没有获得配置文件信息,那么,就直接返回,然后获得当前运行进程的id
def pid_files(self, **kwargs):
"""Get pid files for this server
:param: number, if supplied will only lookup the nth server
:returns: list of pid files
"""
if self.conf is not None:
pid_files = search_tree(self.run_dir, '%s*' % self.server,
exts=[self.conf + '.pid',
self.conf + '.pid.d'])
else:
#这里就查找运行目录下的proxy-server.pid account-server文件夹下的1.pid...
pid_files = search_tree(self.run_dir, '%s*' % self.server)
if kwargs.get('number', 0):
conf_files = self.conf_files(**kwargs)
# filter pid_files to match the index of numbered conf_file
pid_files = [pid_file for pid_file in pid_files if
self.get_conf_file_name(pid_file) in conf_files]
return pid_files
def iter_pid_files(self, **kwargs):
"""Generator, yields (pid_file, pids)
"""
for pid_file in self.pid_files(**kwargs):
yield pid_file, int(open(pid_file).read().strip())
def signal_pids(self, sig, **kwargs):
"""Send a signal to pids for this server
:param sig: signal to send
:returns: a dict mapping pids (ints) to pid_files (paths)
"""
pids = {}
for pid_file, pid in self.iter_pid_files(**kwargs):
try:
if sig != signal.SIG_DFL:
print _('Signal %s pid: %s signal: %s') % (self.server,
pid, sig)
os.kill(pid, sig)
except OSError as e:
if e.errno == errno.ESRCH:
# pid does not exist
if kwargs.get('verbose'):
print _("Removing stale pid file %s") % pid_file
remove_file(pid_file)
elif e.errno == errno.EPERM:
print _("No permission to signal PID %d") % pid
else:
# process exists
pids[pid] = pid_file
#这是一个键值对,key是pid,value是记录id的文件路径
return pids
def get_running_pids(self, **kwargs):
"""Get running pids
:returns: a dict mapping pids (ints) to pid_files (paths)
"""
return self.signal_pids(signal.SIG_DFL, **kwargs) # send noop
到此,获得id或许是空的,但是路径已经确定了
already_started = False
for pid, pid_file in pids.items():#遍历获得的键值对pids
#根据pid_file找到conf_file
conf_file = self.get_conf_file_name(pid_file)#根据pid的文件路径路径获得配置文件路径,
# for legacy compat you can't start other servers if one server is
# already running (unless -n specifies which one you want), this
# restriction could potentially be lifted, and launch could start
# any unstarted instances
#如果conf_file已经在conf_files当中那么,说明已经运行,直接返回
if conf_file in conf_files:
already_started = True
print _("%s running (%s - %s)") % (self.server, pid, conf_file)
elif not kwargs.get('number', 0):
already_started = True
print _("%s running (%s - %s)") % (self.server, pid, pid_file)
if already_started:
print _("%s already started...") % self.server
return {}
如果alread_started已经是TRUE,那么该服务已经启动直接返回即可,如果没有,则继续运行
if self.server not in START_ONCE_SERVERS:
kwargs['once'] = False
#根据每个配置文件来为每个服务启动一个子进程,并返回相应的进程号
#最后把启动的子进程的进程号pid和相应服务的配置文件conf_file一一对应起来;
#为服务启动子进程主要是通过语句:pid = self.spawn(conf_file, **kwargs)
pids = {}
for conf_file in conf_files:
if kwargs.get('once'):
msg = _('Running %s once') % self.server
else:
msg = _('Starting %s') % self.server
print '%s...(%s)' % (msg, conf_file)
try:
#运行到这里说明服务还没有启动,需要去启动,然后我们根据该服务的配置文件去启动该服务,
#如果是account-server则遍历/etc/swift/account-server下的配置文件(*.conf)去启动
#如果没有则proxy-server,则查找proxy-server.conf和swift.conf进行启动,然后返回启动进程的
#id,根据id获得配置文件路径进行存储
pid = self.spawn(conf_file, **kwargs)
except OSError as e:
if e.errno == errno.ENOENT:
#TODO(clayg): should I check if self.cmd exists earlier?
print _("%s does not exist") % self.cmd
break
else:
raise
pids[pid] = conf_file
return pids
接下来看看最主要的函数spawn,该函数进行了进程的启动
def spawn(self, conf_file, once=False, wait=True, daemon=True, **kwargs):
"""Launch a subprocess for this server.
为服务启动一个子进程;
返回衍生进程的pid;
:param conf_file: path to conf_file to use as first arg
:param once: boolean, add once argument to command
:param wait: boolean, if true capture stdout with a pipe
:param daemon: boolean, if false ask server to log to console
:returns : the pid of the spawned process
"""
args = [self.cmd, conf_file]#例如['swift-proxy-server','/etc/swift/proxy-server.conf']
if once:
args.append('once')
if not daemon:
# ask the server to log to console
args.append('verbose')
# figure out what we're going to do with stdio
if not daemon:
# do nothing, this process is open until the spawns close anyway
re_out = None
re_err = None
else:
re_err = subprocess.STDOUT
if wait:
# we're going to need to block on this...
re_out = subprocess.PIPE
else:
re_out = open(os.devnull, 'w+b')
# Popen:创建新的Popen实例;
proc = subprocess.Popen(args, stdout=re_out, stderr=re_err)
# 转换conf_file到相应的pid_file;
pid_file = self.get_pid_file_name(conf_file)
#根据配置文件将id号写入*.pid文件当中
write_file(pid_file, proc.pid)
#将子进程加入到进程当中
self.procs.append(proc)
# 返回conf_file相应的pid_file;
return proc.pid
这样就完成了服务的启动,也即,最后根据一种转换,根据配置文件的路径去查找安装路径相应的执行文件然后根据启动的进程信息保存pid和pid_file,这样一个服务就正式启动了