OpenStack Swift源码分析(三)proxy服务启动

分析了服务启动的架构,下面看一下服务启动的源码.分析的不好,还请指教

    创建好了builder文件和ring文件之后,下一步的操作就是启动服务了,通常启动单独的服务会有单独的命令,例如swift-proxy-server start等,但是一般我们使用swift-init命令,因为大多数情况下所有的服务会安装在同一台机器上,或者是存储服务会安装在同一台机器上。swift-init会设计到/swift/common/manager.py中的Manager Server类,Manager类来管理命令,Server是相应服务的实例,其中包括对服务的一些操作。

swift-init源码:

01 USAGE = """%prog <server> [<server> ...] <command> [options]#获得命令提示
02  
03 Commands:
04 """ + '\n'.join(["%16s%s" % for in Manager.list_commands()])
05  
06 def main():
07     parser = OptionParser(USAGE)
08     parser.add_option('-v''--verbose', action="store_true",#设置参数#
09                       default=Falsehelp="display verbose output")
10     parser.add_option('-w''--no-wait', action="store_false", dest="wait",
11                       default=Truehelp="won't wait for server to start "
12                       "before returning")
13     parser.add_option('-o''--once', action="store_true",
14                       default=Falsehelp="only run one pass of daemon")
15     # this is a negative option, default is options.daemon = True
16     parser.add_option('-n''--no-daemon', action="store_false", dest="daemon",
17                       default=Truehelp="start server interactively")
18     parser.add_option('-g''--graceful', action="store_true",
19                       default=Falsehelp="send SIGHUP to supporting servers")
20     parser.add_option('-c''--config-num', metavar="N"type="int",
21                       dest="number", default=0,
22                       help="send command to the Nth server only")
23     options, args = parser.parse_args()
24  
25     if len(args) < 2:#长度小于2,也就是输入类似swift_init proxy 没有输入命令,会提示。
26         parser.print_help()
27         print 'ERROR: specify server(s) and command'
28         return 1
29  
30     command = args[-1#切分命令和服务
31     servers = args[:-1]
32  
33     # this is just a silly swap for me cause I always try to "start main"
34     commands = dict(Manager.list_commands()).keys()#判断命令是否合法
35     if command not in commands and servers[0in commands:
36         servers.append(command)
37         command = servers.pop(0)
38  
39     manager = Manager(servers)#初始化manager实例
40     try:
41         status = manager.run_command(command, **options.__dict__)#执行命令函数
42     except UnknownCommandError:
43         parser.print_help()
44         print 'ERROR: unknown command, %s' % command
45         status = 1
46  
47     return 1 if status else 0
48  
49  
50 if __name__ == "__main__":
51     sys.exit(main())
    其中使用了功能比较强大的optpartse(当然对于我这种初学者,强不强大都一样0.0),值得注意的是parse.add_option方法添加的参数,非常值得注意,因为一般情况下我们不会输入这些参数,但是它们会制止带入到后面的方法中,来执行相应的逻辑。其中manager.run_commond()来执行命令。
1 def run_command(self, cmd, **kwargs):
2         """Find the named command and run it
3  
4         :param cmd: the command name to run
5  
6         """
7         = self.get_command(cmd)#返回命令,例如start,然后执行相应的方法
8         return f(**kwargs)
其实run_command方法就是调用相应命令对应的方法比如start().
01 def get_command(self, cmd):
02         """Find and return the decorated method named like cmd
03  
04         :param cmd: the command to get, a string, if not found raises
05                     UnknownCommandError
06  
07         """
08         cmd = cmd.lower().replace('-''_')
09         try:
10             = getattr(self, cmd)
11         except AttributeError:
12             raise UnknownCommandError(cmd)
13         if not hasattr(f, 'publicly_accessible'):
14             raise UnknownCommandError(cmd)
15         return f
16  
17     @classmethod #类方法,没有实例也可以执行
18     def list_commands(cls):
19         """Get all publicly accessible commands
20  
21         :returns: a list of string tuples (cmd, help), the method names who are
22                   decorated as commands
23         """
24         get_method = lambda cmd: getattr(cls, cmd)
25         return sorted([(x.replace('_''-'), get_method(x).__doc__.strip())
26                        for in dir(clsif
27                        getattr(get_method(x), 'publicly_accessible'False)])
28  
29 @command
30     def start(self**kwargs):
31         """starts a server
32         """
33         setup_env()#进行初始化,
34         status = 0
35  
36         for server in self.servers:#不同的server执行launch()方法
37             server.launch(**kwargs)
38         if not kwargs.get('daemon'True):#默认为True
39             for server in self.servers:
40                 try:
41                     status += server.interact(**kwargs)#与wait类似,收集程序返回的信息。
42                 except KeyboardInterrupt:
43                     print _('\nuser quit')
44                     self.stop(**kwargs)
45                     break
46         elif kwargs.get('wait'True):
47             for server in self.servers:
48                 status += server.wait(**kwargs)#接受服务启动进程返回的信息
49         return status
start方法中会调用launch方法
01 def launch(self**kwargs):
02         """
03         Collect conf files and attempt to spawn the processes for this server
04         """
05         conf_files = self.conf_files(**kwargs)
06         if not conf_files:
07             return []
08  
09         pids = self.get_running_pids(**kwargs)#查看已经启动的服务
10  
11         already_started = False
12         for pid, pid_file in pids.items():#做判断,如果已经启动,返回已经启动。
13             conf_file = self.get_conf_file_name(pid_file)
14             # for legacy compat you can't start other servers if one server is
15             # already running (unless -n specifies which one you want), this
16             # restriction could potentially be lifted, and launch could start
17             # any unstarted instances
18             if conf_file in conf_files:
19                 already_started = True
20                 print _("%s running (%s - %s)"% (self.server, pid, conf_file)
21             elif not kwargs.get('number'0):
22                 already_started = True
23                 print _("%s running (%s - %s)"% (self.server, pid, pid_file)
24  
25         if already_started:
26             print _("%s already started..."% self.server
27             return []
28  
29         if self.server not in START_ONCE_SERVERS:#默认是false,如果你的服务不在START_ONCE_SERVER,但是你社会自了为True,也会被设置为False.
30  
31             kwargs['once'= False
32  
33         pids = {}
34         for conf_file in conf_files:
35             if kwargs.get('once'):
36                 msg = _('Running %s once'% self.server
37             else:
38                 msg = _('Starting %s'% self.server
39             print '%s...(%s)' % (msg, conf_file)
40             try:
41                 pid = self.spawn(conf_file, **kwargs)#最终启动进程的函数。返回pid
42             except OSError, e:
43                 if e.errno == errno.ENOENT:
44                     # TODO: should I check if self.cmd exists earlier?
45                     print _("%s does not exist"% self.cmd
46                     break
47             pids[pid] = conf_file
48  
49         return pids
launch方法会调用spawn方法最终通过subproess.Popen来启动一个子进程执行命令,相当于swift-proxy-server start
01 def spawn(self, conf_file, once=False, wait=True, daemon=True**kwargs):
02         """Launch a subprocess for this server.
03  
04         :param conf_file: path to conf_file to use as first arg
05         :param once: boolean, add once argument to command
06         :param wait: boolean, if true capture stdout with a pipe
07         :param daemon: boolean, if true ask server to log to console
08  
09         :returns : the pid of the spawned process
10         """
11         args = [self.cmd, conf_file]#命令和配置文件
12         if once:
13             args.append('once')
14         if not daemon:
15             # ask the server to log to console
16             args.append('verbose')
17  
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         proc = subprocess.Popen(args, stdout=re_out, stderr=re_err)#启动子进程,例如,如果你输入的是swift-init proxy start,实际上他会执行swift-proxy-server start 把配置文件带入
31         pid_file = self.get_pid_file_name(conf_file)
32         write_file(pid_file, proc.pid)#写入pid_file未见
33         self.procs.append(proc)
34         return proc.pid返回pid

swift-proxy-server中就两行代码,调用/swift/common/wsgi.py中的run_wsgi方法。

1 if __name__ == '__main__':
2     conf_file, options = parse_options()
3   run_wsgi(conf_file, 'proxy-server', default_port=8080**options)

其实最终启动服务的是wsgi.py中的run_wsgi方法(因为实际上swift就是提供一个wsgi服务)。

001 def run_wsgi(conf_file, app_section, *args, **kwargs):
002     """
003     Loads common settings from conf, then instantiates app and runs
004     the server using the specified number of workers.
005  
006     :param conf_file: Path to paste.deploy style configuration file
007     :param app_section: App name from conf file to load config from
008     """
009  
010     try:
011         conf = appconfig('config:%s' % conf_file, name=app_section)#获取conf_file中关于proxy-server的配置
012     except Exception, e:
013         print "Error trying to load config %s: %s" % (conf_file, e)
014         return
015     validate_configuration()#验证是否设置HASH_PATH_SUFFIX
016  
017     # pre-configure logger#日志设置
018     log_name = conf.get('log_name', app_section)
019     if 'logger' in kwargs:
020         logger = kwargs.pop('logger')
021     else:
022         logger = get_logger(conf, log_name,
023             log_to_console=kwargs.pop('verbose'False), log_route='wsgi')
024  
025     # disable fallocate if desired#新版本加入的函数 ,调用系统函数,预留物理空间,目前理解,并不准确。
026   if conf.get('disable_fallocate''no').lower() in TRUE_VALUES:
027         disable_fallocate()
028  
029     # bind to address and port#生产socket
030     sock = get_socket(conf, default_port=kwargs.get('default_port'8080))
031     # remaining tasks should not require elevated privileges#剩下的任务不需要高级的权限。
032     drop_privileges(conf.get('user''swift'))
033  
034     # Ensure the application can be loaded before proceeding.#确保应用可以被加载
035     loadapp('config:%s' % conf_file, global_conf={'log_name': log_name})
036  
037     # redirect errors to logger and close stdio#直接将错误输入到日志中,关闭标准输出。
038     capture_stdio(logger)
039  
040     def run_server():#启动server函数
041         wsgi.HttpProtocol.default_request_version = "HTTP/1.0"
042         # Turn off logging requests by the underlying WSGI software.
043         wsgi.HttpProtocol.log_request = lambda *a: None
044         # Redirect logging other messages by the underlying WSGI software.
045         wsgi.HttpProtocol.log_message = \
046             lambda s, f, *a: logger.error('ERROR WSGI: ' + % a)
047         wsgi.WRITE_TIMEOUT = int(conf.get('client_timeout'or 60)
048         eventlet.hubs.use_hub('poll')
049         eventlet.patcher.monkey_patch(all=False, socket=True)
050         monkey_patch_mimetools()
051         app = loadapp('config:%s' % conf_file,#取得相应的app和filter
052                       global_conf={'log_name': log_name})
053         pool = GreenPool(size=1024)
054         try:
055             wsgi.server(sock, app, NullLogger(), custom_pool=pool)#利用eventlet.wsgi.server()方法 启动服务
056         except socket.error, err:
057             if err[0] != errno.EINVAL:
058                 raise
059         pool.waitall()
060  
061     worker_count = int(conf.get('workers''1'))
062     # Useful for profiling [no forks].
063     if worker_count == 0:#如果worker_count等于0直接运行server
064         run_server()
065         return
066 #之后的程序会根据不同的方式创建,进程来执行run_server()方法
067     def kill_children(*args):
068         """Kills the entire process group."""
069         logger.error('SIGTERM received')
070         signal.signal(signal.SIGTERM, signal.SIG_IGN)
071         running[0= False
072         os.killpg(0, signal.SIGTERM)
073  
074     def hup(*args):
075         """Shuts down the server, but allows running requests to complete"""
076         logger.error('SIGHUP received')
077         signal.signal(signal.SIGHUP, signal.SIG_IGN)
078         running[0= False
079  
080     running = [True]
081     signal.signal(signal.SIGTERM, kill_children)
082     signal.signal(signal.SIGHUP, hup)
083     children = []
084     while running[0]:
085         while len(children) < worker_count:
086             pid = os.fork()
087             if pid == 0:
088                 signal.signal(signal.SIGHUP, signal.SIG_DFL)
089                 signal.signal(signal.SIGTERM, signal.SIG_DFL)
090                 run_server()
091                 logger.notice('Child %d exiting normally' % os.getpid())
092                 return
093             else:
094                 logger.notice('Started child %s' % pid)
095                 children.append(pid)
096         try:
097             pid, status = os.wait()
098             if os.WIFEXITED(status) or os.WIFSIGNALED(status):
099                 logger.error('Removing dead child %s' % pid)
100                 children.remove(pid)
101         except OSError, err:
102             if err.errno not in (errno.EINTR, errno.ECHILD):
103                 raise
104         except KeyboardInterrupt:
105             logger.notice('User quit')
106             break
107     greenio.shutdown_safe(sock)
108     sock.close()
109     logger.notice('Exited')
run_wsgi方法相当复杂,最终通过内部方法run_server()函数中的,wsgi.server()启动服务,其中使用eventlet库,还用进程替换,等操作,还一些日志,已经初始化的所及操作。由于我本身初学python,所以分析的是很细致。值得注意的是,swift配置文件适用使用paste.deploy方式来书写,其中的各个服务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值