分析了服务启动的架构,下面看一下服务启动的源码.分析的不好,还请指教
创建好了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" % x for x in Manager.list_commands()]) |
05 |
06 | def main(): |
07 | parser = OptionParser(USAGE) |
08 | parser.add_option( '-v' , '--verbose' , action = "store_true" , #设置参数# |
09 | default = False , help = "display verbose output" ) |
10 | parser.add_option( '-w' , '--no-wait' , action = "store_false" , dest = "wait" , |
11 | default = True , help = "won't wait for server to start " |
12 | "before returning" ) |
13 | parser.add_option( '-o' , '--once' , action = "store_true" , |
14 | default = False , help = "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 = True , help = "start server interactively" ) |
18 | parser.add_option( '-g' , '--graceful' , action = "store_true" , |
19 | default = False , help = "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[ 0 ] in 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()) |
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 | f = self .get_command(cmd) #返回命令,例如start,然后执行相应的方法 |
8 | return f( * * kwargs) |
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 | f = 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 x in dir ( cls ) if |
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 |
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 |
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: ' + f % 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' ) |