分享

OpenStack Swift源码分析(2)----swift服务启动源码分析之二

tntzbzc 发表于 2014-11-20 15:34:51 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 18158
本帖最后由 pig2 于 2014-11-21 15:08 编辑
问题导读

1、return proc.pid这段代码主要做了哪些事?
2、launch方法依次启动了哪些服务?
3、如何通过配置文件来判断服务是否已经启动?






继续看方法def start(self, **kwargs):
  1.     def start(self, **kwargs):  
  2.             """
  3.             启动一个服务;
  4.             """  
  5.             setup_env()  
  6.             status = 0  
  7.       
  8.             for server in self.servers:  
  9.                 server.launch(**kwargs)  
  10.             if not kwargs.get('daemon', True):  
  11.                 for server in self.servers:  
  12.                     try:  
  13.                         status += server.interact(**kwargs)  
  14.                     except KeyboardInterrupt:  
  15.                         print _('\nuser quit')  
  16.                         self.stop(**kwargs)  
  17.                         break  
  18.             elif kwargs.get('wait', True):  
  19.                 for server in self.servers:  
  20.                     status += server.wait(**kwargs)  
  21.             return status  我们先来看代码:
  22. for server in self.servers:
  23.     server.launch(**kwargs)前面分析知道,在类Manager初始化的过程中,得到:
  24. self.server = ['proxy-server', 'account-server', 'container-server', 'object-server']
复制代码


所以这个for循环实现了通过调用launch方法依次启动这四个服务;
注:示例:
  1. kwargs = {'run_dir': '/var/run/swift', 'daemon': True, 'verbose': False, 'number': 0, 'kill_wait': 15, 'graceful': False, 'once': False, 'wait': True}
复制代码

我们具体来看方法launch:
  1. def launch(self, **kwargs):
  2.         """
  3.         Collect conf files and attempt to spawn the processes for this server
  4.         """
  5.         conf_files = self.conf_files(**kwargs)
  6.         if not conf_files:
  7.             return []
  8.         # 获取运行的pids;
  9.         # 返回一个pids映射到pid_files的字典;
  10.         pids = self.get_running_pids(**kwargs)
  11.         already_started = False
  12.         for pid, pid_file in pids.items():
  13.             
  14.             # 转换pid_file到相应的conf_file;
  15.             # 返回pid_file对应的conf_file;
  16.             conf_file = self.get_conf_file_name(pid_file)
  17.             
  18.             if conf_file in conf_files:
  19.                 already_started = True
  20.                 print _("%s running (%s - %s)") % (self.server, pid, conf_file)
  21.             
  22.             elif not kwargs.get('number', 0):
  23.                 already_started = True
  24.                 print _("%s running (%s - %s)") % (self.server, pid, pid_file)
  25.         if already_started:
  26.             print _("%s already started...") % self.server
  27.             return []
  28.         
  29.         if self.server not in START_ONCE_SERVERS:
  30.             kwargs['once'] = False
  31.         pids = {}
  32.         for conf_file in conf_files:
  33.             if kwargs.get('once'):
  34.                 msg = _('Running %s once') % self.server
  35.             else:
  36.                 msg = _('Starting %s') % self.server
  37.             print '%s...(%s)' % (msg, conf_file)
  38.             
  39.             try:
  40.                 # conf_file:/etc/swift/proxy-server.conf
  41.                 # kwargs = {'run_dir':'/var/run/swift','damon':True,'verbose':False,'number':0,'kill_wait':15,'graceful':False,'once':False,'wait':True}
  42.                 pid = self.spawn(conf_file, **kwargs)
  43.             except OSError, e:
  44.                 if e.errno == errno.ENOENT:
  45.                     # TODO: should I check if self.cmd exists earlier?
  46.                     print _("%s does not exist") % self.cmd
  47.                     break
  48.             pids[pid] = conf_file
  49.         return pids这个方法主要实现了根据服务获取具体的配置文件,并试图通过调用方法spawn来为服务的运行启动一个子进程,方法的最后返回了这个子进程的进程号;
复制代码


首先来看代码:
  1. conf_files = self.conf_files(**kwargs)
  2. if not conf_files:
  3.     return []
复制代码


实现的是通过传入的kwargs获取服务匹配的配置文件,如果没有配置文件直接返回;
调试运行示例:
  1. conf_files = ['/etc/swift/proxy-server.conf']
复制代码

再来看代码:
  1. # 获取运行的pids;
  2. # 返回一个pids映射到pid_files的字典;
  3. pids = self.get_running_pids(**kwargs)调试运行示例:
  4. pids = {10955: '/var/run/swift/proxy-server.pid'}
  5. 可见这条语句返回的是正在运行进程的pid号,和具体匹配的pid_file的路径,为后面判断要启动的服务是否已经运行做准备;
复制代码


看下面一段代码,通过配置文件来判断服务是否已经启动:
  1. already_started = False
  2. for pid, pid_file in pids.items():
  3.             
  4.     # 转换pid_file到相应的conf_file;
  5.     # 返回pid_file对应的conf_file;
  6.     conf_file = self.get_conf_file_name(pid_file)
  7.             
  8.     if conf_file in conf_files:
  9.         already_started = True
  10.         print _("%s running (%s - %s)") % (self.server, pid, conf_file)
  11.    
  12.     elif not kwargs.get('number', 0):
  13.         already_started = True
  14.         print _("%s running (%s - %s)") % (self.server, pid, pid_file)
  15. if already_started:
  16.     print _("%s already started...") % self.server
  17.     return []前面我们通过kwargs获取了正在运行的服务的pid值和相应的pid_file,比如:
  18. pids = {10955: '/var/run/swift/proxy-server.pid'}
复制代码

这里遍历pids这个字典,做了以下几件事:
(1)通过pid_file获取对应服务的配置文件conf_file;
(2)判断获取的conf_file是否包含在已经获取的conf_files中,如果是的话,说明要启动的这个服务已经处于运行状态;
(3)如果(2)判断为假,继续判断传进来的参数kwargs中,是否包含'number',这一选项,它的值是否为0,如果是的话,也说明要启动的这个服务已经处于运行状态;
最后,如果already_started的值为真,说明服务已经运行了,所以打印输出提示后,直接返回;
继续来看代码,如何为服务来启动一个子进程:
  1. pids = {}
  2. for conf_file in conf_files:
  3.    if kwargs.get('once'):
  4.        msg = _('Running %s once') % self.server
  5.    else:
  6.        msg = _('Starting %s') % self.server
  7.    print '%s...(%s)' % (msg, conf_file)
  8.             
  9.    try:
  10.        # conf_file:/etc/swift/proxy-server.conf
  11.        # kwargs = {'run_dir':'/var/run/swift','damon':True,'verbose':False,'number':0,'kill_wait':15,'graceful':False,'once':False,'wait':True}
  12.        pid = self.spawn(conf_file, **kwargs)
  13.    except OSError, e:
  14.        if e.errno == errno.ENOENT:
  15.            # TODO: should I check if self.cmd exists earlier?
  16.            print _("%s does not exist") % self.cmd
  17.            break
  18.    pids[pid] = conf_file这段代码是遍历conf_files,根据每个配置文件来为每个服务启动一个子进程,并返回相应的进程号,最后把启动的子进程的进程号pid和相应服务的配置文件conf_file一一对应起来;
  19. 为服务启动子进程主要是通过语句:pid = self.spawn(conf_file, **kwargs)
复制代码


我们具体来看方法spawn:
  1. def spawn(self, conf_file, once=False, wait=True, daemon=True, **kwargs):
  2.         """
  3.         为服务启动一个子进程;
  4.         返回衍生进程的pid;
  5.         
  6.         # 调用之一传进来的参数:
  7.         # conf_file:/etc/swift/proxy-server.conf
  8.         # kwargs = {'run_dir':'/var/run/swift','damon':True,'verbose':False,'number':0,'kill_wait':15,'graceful':False,'once':False,'wait':True}     
  9.         """
  10.         args = [self.cmd, conf_file]
  11.         
  12.         if once:
  13.             args.append('once')
  14.          
  15.         if not daemon:
  16.             # ask the server to log to console
  17.             args.append('verbose')
  18.         # figure out what we're going to do with stdio
  19.         if not daemon:
  20.             # do nothing, this process is open until the spawns close anyway
  21.             re_out = None
  22.             re_err = None
  23.         else:
  24.             re_err = subprocess.STDOUT
  25.             if wait:
  26.                 # we're going to need to block on this...
  27.                 re_out = subprocess.PIPE
  28.             else:
  29.                 re_out = open(os.devnull, 'w+b')
  30.                
  31.         # Popen:创建新的Popen实例;
  32.         proc = subprocess.Popen(args, stdout=re_out, stderr=re_err)
  33.         
  34.         # 转换conf_file到相应的pid_file;
  35.         # 返回conf_file相应的pid_file;
  36.         pid_file = self.get_pid_file_name(conf_file)
  37.         
  38.         # 写proc.pid内容到pid_file;
  39.         write_file(pid_file, proc.pid)
  40.         
  41.         self.procs.append(proc)
  42.         
  43.         return proc.pid这个方法完成的是调用方法Popen实现把一个服务放到一个子进程中去运行;
复制代码


来具体看代码:
  1. args = [self.cmd, conf_file]
  2.         
  3.         if once:
  4.             args.append('once')
  5.          
  6.         if not daemon:
  7.             # ask the server to log to console
  8.             args.append('verbose')这部分代码完成的是把cmd和配置文件conf_file路径以及其他一些参数整合到一起,组成命令参数args,将会用于在后面传入到方法Popen中,实现在子进程中运行args中指定的服务;
复制代码


调试示例:
  1. args = ['swift-proxy-server', '/etc/swift/proxy-server.conf']
复制代码

即指定了要运行的服务和它的配置文件;
继续看代码:
  1.         # figure out what we're going to do with stdio
  2.         if not daemon:
  3.             # do nothing, this process is open until the spawns close anyway
  4.             re_out = None
  5.             re_err = None
  6.         else:
  7.             re_err = subprocess.STDOUT
  8.             if wait:
  9.                 # we're going to need to block on this...
  10.                 re_out = subprocess.PIPE
  11.             else:
  12.                 re_out = open(os.devnull, 'w+b')这段代码实现的是明确一些参数用于告诉子进程要做什么,但是具体的参数含义还没有研究;
复制代码

来看最关键的一条语句:
  1. # Popen:创建新的Popen实例;
  2. proc = subprocess.Popen(args, stdout=re_out, stderr=re_err)这里调用了Popen方法实现了把指定的服务放到一个子进程中来运行;
复制代码

这里传入了参数args指定了要运行的服务和相应服务的配置文件,这是比较关键的地方;
这个方法中实际上分别调用了/usr/bin/swift-proxy-server、/usr/bin/swift-container-server、/usr/bin/swift-account-server、/usr/bin/swift-object-server等程序中的main方法,实现了对指定服务的启动运行;我们来深入跟踪一下args这个参数,来看看是如何实现调用这几个服务代码的:
  1. class Popen(object):
  2.     def __init__(self, args, bufsize=0, executable=None,
  3.                  stdin=None, stdout=None, stderr=None,
  4.                  preexec_fn=None, close_fds=False, shell=False,
  5.                  cwd=None, env=None, universal_newlines=False,
  6.                  startupinfo=None, creationflags=0):
  7.         """Create new Popen instance."""
  8.         ......
  9.         self._execute_child(args, executable, preexec_fn, close_fds,
  10.                             cwd, env, universal_newlines,
  11.                             startupinfo, creationflags, shell,
  12.                             p2cread, p2cwrite,
  13.                             c2pread, c2pwrite,
  14.                             errread, errwrite)def _execute_child(self, args, executable, preexec_fn, close_fds,
  15.                            cwd, env, universal_newlines,
  16.                            startupinfo, creationflags, shell,
  17.                            p2cread, p2cwrite,
  18.                            c2pread, c2pwrite,
  19.                            errread, errwrite):
  20.     """Execute program (POSIX version)"""
  21.     if isinstance(args, types.StringTypes):
  22.         args = [args]
  23.     else:
  24.         args = list(args)
  25.     if shell:
  26.         args = ["/bin/sh", "-c"] + args
  27.         if executable:
  28.             args[0] = executable
  29.     if executable is None:
  30.         executable = args[0]
  31.     ......
  32.     try:
  33.         try:
  34.         
  35.         ......
  36.         if self.pid == 0:
  37.             try:
  38.                
  39.                 ......
  40.                
  41.                 if env is None:
  42.                     os.execvp(executable, args)
  43.                 else:
  44.                     os.execvpe(executable, args, env)
  45.     ......调试运行示例:
  46. executable = swift-proxy-server
  47. args = ['swift-proxy-server', '/etc/swift/proxy-server.conf']
  48. env = None
  49. def execvp(file, args):
  50.     _execvpe(file, args)def _execvpe(file, args, env=None):
  51.     if env is not None:
  52.         func = execve
  53.         argrest = (args, env)
  54.     else:
  55.         func = execv
  56.         argrest = (args,)
  57.         env = environ
  58.     head, tail = path.split(file)
  59.     if head:
  60.         func(file, *argrest)
  61.         return
  62.     if 'PATH' in env:
  63.         envpath = env['PATH']
  64.     else:
  65.         envpath = defpath
  66.     PATH = envpath.split(pathsep)
  67.     saved_exc = None
  68.     saved_tb = None
  69.     for dir in PATH:
  70.         fullname = path.join(dir, file)
  71.         try:
  72.             func(fullname, *argrest)
  73.    
  74.         ......
  75.     ......调试运行示例:
  76. argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
  77. fullname = /usr/local/sbin/swift-proxy-server
  78. argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
  79. fullname = /usr/local/bin/swift-proxy-server
  80. argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
  81. fullname = /usr/sbin/swift-proxy-server
  82. argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
  83. fullname = /usr/bin/swift-proxy-server
  84. argrest = (['swift-proxy-server', '/etc/swift/proxy-server.conf'],)
复制代码

我们可以看到,程序实现了依次查找/usr/local/sbin/、 /usr/local/bin/、/usr/sbin/和/usr/bin/等路径,查找swift-proxy-server文件,并执行其中的main方法;
我会在下一篇博客中具体分析,swift-proxy-server中的main方法是怎样实现运行proxy服务的;
继续来看方法spawn的代码:
  1. # 转换conf_file到相应的pid_file;
  2. # 返回conf_file相应的pid_file;
  3. pid_file = self.get_pid_file_name(conf_file)
  4.         
  5. # 写proc.pid内容到pid_file;
  6. write_file(pid_file, proc.pid)
  7.         
  8. self.procs.append(proc)
复制代码

        
return proc.pid这段代码主要做了一下几件事:
(1)通过conf_file获取相应的pid_file;
(2)把新启动的子进程的pid写进到文件pid_file之中;
(3)把新启动的子进程的对象加入到procs之中;
(4)返回新启动的子进程的pid值;
到这里方法spawn分析完成,我们大体可以了解了系统是怎样启动一个子进程来运行指定的服务的;
我们回到方法lauch,方法最后返回了所启动的一系列子进程的集合pids;
我们回到方法start,方法的最后根据参数设定,来决定执行方法interact或者方法wait,来等待子进程的运行结束;
最后方法start返回子进程的运行状态给swift-init中的方法main,即解析完成方法main中的语句:
status = manager.run_command(command, **options.__dict__)
至此,整个swift服务的启动过程我们分析完成,我将会在下一篇博客中具体分析swift-proxy-server中的main方法是如何实现运行proxy服务的。
博文中不免有不正确的地方,欢迎朋友们不吝批评指正,谢谢大家了!

相关内容:
OpenStack Swift源码分析(1)----swift服务启动源码分析之一

OpenStack Swift源码分析(2)----swift服务启动源码分析之二

OpenStack Swift源码分析(3)----swift服务启动源码分析之三


没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条