swift源代码解读(三) proxy-server的启动

之前说过了,swift是怎样根据配置文件进行服务的启动的,现在来说一下proxy-server的启动过程,到现在为止,系统已经找到了swift-proxy-server文件,并进行执行,看下该文件的代码

import sys
from swift.common.utils import parse_options
from swift.common.wsgi import run_wsgi

if __name__ == '__main__':
    conf_file, options = parse_options()
    sys.exit(run_wsgi(conf_file, 'proxy-server', **options))

也即主要的函数run_wsgi文件是在common下的wsgi文件中的
def run_wsgi(conf_path, app_section, *args, **kwargs):
    """
	运行服务制定的进程数目
    Runs the server using the specified number of workers.
    :param conf_path: Path to paste.deploy style configuration file/directory
    :param app_section: App name from conf file to load config from,制定的配置文件名称‘proxy_server’
    :returns: 0 if successful, nonzero otherwise,返回0表示成功,非0 失败
    """
    # Load configuration, Set logger and Load request processor
    try:
	    #_initrp函数定义在514行,根据配置加载日志记录器,还有,指定的/etc/swift/swift-proxy-server.conf文件中的节proxy-server中的内容记录在了conf中
        (conf, logger, log_name) = \
            _initrp(conf_path, app_section, *args, **kwargs)
    except ConfigFileError as e:
        print(e)
        return 1

    # bind to address and port  绑定地址和端口号
    try:
	  
        sock = get_socket(conf)
       #至此已经根据配置文件将端口ip地址绑定完毕
    except ConfigFilePortError:
        msg = 'bind_port wasn\'t properly set in the config file. ' \
              'It must be explicitly set to a valid port number.'
        logger.error(msg)
        print(msg)
        return 1
    # remaining tasks should not require elevated privileges,设置当前进程运行的用户名和所在组,工作目录为/
    drop_privileges(conf.get('user', 'swift'))

    # Ensure the configuration and application can be loaded before proceeding.
	#
    global_conf = {'log_name': log_name}
    if 'global_conf_callback' in kwargs:
        kwargs['global_conf_callback'](conf, global_conf)
    loadapp(conf_path, global_conf=global_conf)

    # set utils.FALLOCATE_RESERVE if desired
    reserve = int(conf.get('fallocate_reserve', 0))
    if reserve > 0:
        utils.FALLOCATE_RESERVE = reserve
    # redirect errors to logger and close stdio 调用方法capture_stdio实现日志记录未处理的异常,关闭标准输入输出,获取标准输出和异常信息的功能;
	#输入输出重定向改向日志记录进行输出
    capture_stdio(logger)

    worker_count = config_auto_int_value(conf.get('workers'), CPU_COUNT)#获得CPU数目,或者默认为1

    # Useful for profiling [no forks].
    if worker_count == 0:
        run_server(conf, logger, sock, global_conf=global_conf)
        return 0

    def kill_children(*args):
        """Kills the entire process group."""
        logger.error('SIGTERM received')
        signal.signal(signal.SIGTERM, signal.SIG_IGN)
        running[0] = False
        os.killpg(0, signal.SIGTERM)

    def hup(*args):
        """Shuts down the server, but allows running requests to complete"""
        logger.error('SIGHUP received')
        signal.signal(signal.SIGHUP, signal.SIG_IGN)
        running[0] = False

    running = [True]
    signal.signal(signal.SIGTERM, kill_children)
    signal.signal(signal.SIGHUP, hup)
    children = []
	#开启worker_count个进程,每个进程都有一个wscgi,每个wscgi的最大连接数默认是1000
    while running[0]:
        while len(children) < worker_count:
            pid = os.fork()
            if pid == 0:
                signal.signal(signal.SIGHUP, signal.SIG_DFL)
                signal.signal(signal.SIGTERM, signal.SIG_DFL)
		#开始运行wsgi和eventlet
                run_server(conf, logger, sock)
                logger.notice('Child %d exiting normally' % os.getpid())
                return 0
            else:
                logger.notice('Started child %s' % pid)
                children.append(pid)
        try:
            pid, status = os.wait()
            if os.WIFEXITED(status) or os.WIFSIGNALED(status):
                logger.error('Removing dead child %s' % pid)
                children.remove(pid)
        except OSError as err:
            if err.errno not in (errno.EINTR, errno.ECHILD):
                raise
        except KeyboardInterrupt:
            logger.notice('User quit')
            break
    greenio.shutdown_safe(sock)
    sock.close()
    logger.notice('Exited')
    return 0
现在说下get_socket函数,
def get_socket(conf):
    """Bind socket to bind ip:port in conf

    :param conf: Configuration dict to read settings from,根据配置文件进行地质端口的绑定,返回套接字

    :returns : a socket object as returned from socket.listen or
               ssl.wrap_socket if conf specifies cert_file
    """
    try:
        bind_port = int(conf['bind_port'])#获得/etc/swift/proxy-server.conf文件中的proxy-server节下的bind_port的内容
    except (ValueError, KeyError, TypeError):
        raise ConfigFilePortError()
    bind_addr = (conf.get('bind_ip', '0.0.0.0'), bind_port)#获得/etc/swift/proxy-server.conf文件中的proxy-server节下的bind_ip的内容如果没有则返回0.0.0.0
    address_family = [addr[0] for addr in socket.getaddrinfo(
        bind_addr[0], bind_addr[1], socket.AF_UNSPEC, socket.SOCK_STREAM)
        if addr[0] in (socket.AF_INET, socket.AF_INET6)][0]
    sock = None
    bind_timeout = int(conf.get('bind_timeout', 30))#获得/etc/swift/proxy-server.conf文件中的bind_timeout的值,如果 没有则返回30
    retry_until = time.time() + bind_timeout#得出下次尝试时间
    warn_ssl = False
    while not sock and time.time() < retry_until:
        try:
            sock = listen(bind_addr, backlog=int(conf.get('backlog', 4096)),#获得/etc/swift/proxy-server.conf文件中的backlog的值,如果 没有则返回4096
                          family=address_family)
            if 'cert_file' in conf:
                warn_ssl = True
                sock = ssl.wrap_socket(sock, certfile=conf['cert_file'],
                                       keyfile=conf['key_file'])
        except socket.error as err:
            if err.args[0] != errno.EADDRINUSE:
                raise
            sleep(0.1)
    if not sock:
        raise Exception(_('Could not bind to %s:%s '
                          'after trying for %s seconds') % (
                              bind_addr[0], bind_addr[1], bind_timeout))
    #设置socket属性
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # in my experience, sockets can hang around forever without keepalive
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    if hasattr(socket, 'TCP_KEEPIDLE'):
        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 600)
    if warn_ssl:
        ssl_warning_message = _('WARNING: SSL should only be enabled for '
                                'testing purposes. Use external SSL '
                                'termination for a production deployment.')
        get_logger(conf).warning(ssl_warning_message)
        print(ssl_warning_message)
    return sock

接下来是进行drop_privileges其主要作用就是根据配置文件进行运行目录的用户ID和组ID的更换,同时改变进程的运行目录,不过为啥?

def drop_privileges(user):
    """
    Sets the userid/groupid of the current process, get session leader, etc.

    :param user: User name to change privileges to
    """
    if os.geteuid() == 0:
        groups = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem]
        os.setgroups(groups)
	#getpwnam获得对应uid的信息
    user = pwd.getpwnam(user)
    os.setgid(user[3])
    os.setuid(user[2])
    os.environ['HOME'] = user[5]
    try:
        os.setsid()
    except OSError:
        pass
    os.chdir('/')   # in case you need to rmdir on where you started the daemon设定当前运行目录/
    os.umask(0o22)  # ensure files are created with the correct privileges,设定当前运行权限

接下来是worker_count的设定,个人理解是进程数目的设定,要么是根据配置文件中的workers进行设定,要么是获得当前服务器的CPU的数目,(类似Nginx啊),接下来就是根据设定的进程数目,开启wsgi服务的数量,接下来最主要的就是run_server函数,它开启了wsgi和eventlet服务
def run_server(conf, logger, sock, global_conf=None):
    # Ensure TZ environment variable exists to avoid stat('/etc/localtime') on
    # some platforms. This locks in reported times to the timezone in which
    # the server first starts running in locations that periodically change
    # timezones.
    os.environ['TZ'] = time.strftime("%z", time.gmtime())

    wsgi.HttpProtocol.default_request_version = "HTTP/1.0"
    # Turn off logging requests by the underlying WSGI software.
    wsgi.HttpProtocol.log_request = lambda *a: None
    # Redirect logging other messages by the underlying WSGI software.
    wsgi.HttpProtocol.log_message = \
        lambda s, f, *a: logger.error('ERROR WSGI: ' + f % a)
    wsgi.WRITE_TIMEOUT = int(conf.get('client_timeout') or 60)

    eventlet.hubs.use_hub(get_hub())
    eventlet.patcher.monkey_patch(all=False, socket=True)
    eventlet_debug = config_true_value(conf.get('eventlet_debug', 'no'))
    eventlet.debug.hub_exceptions(eventlet_debug)
    wsgi_logger = NullLogger()
    if eventlet_debug:
        # let eventlet.wsgi.server log to stderr
        wsgi_logger = None
    # utils.LogAdapter stashes name in server; fallback on unadapted loggers
    if not global_conf:
        if hasattr(logger, 'server'):
            log_name = logger.server
        else:
            log_name = logger.name
        global_conf = {'log_name': log_name}
    app = loadapp(conf['__file__'], global_conf=global_conf)
	#初始化最大连接数默认1024
    max_clients = int(conf.get('max_clients', '1024'))
    pool = RestrictedGreenPool(size=max_clients)
    try:
        # Disable capitalizing headers in Eventlet if possible.  This is
        # necessary for the AWS SDK to work with swift3 middleware.
        argspec = inspect.getargspec(wsgi.server)
        if 'capitalize_response_headers' in argspec.args:
            wsgi.server(sock, app, wsgi_logger, custom_pool=pool,
                        capitalize_response_headers=False)
        else:
            wsgi.server(sock, app, wsgi_logger, custom_pool=pool)
    except socket.error as err:
        if err[0] != errno.EINVAL:
            raise
    pool.waitall()#等待所有进程执行完毕


设定wsgi 和eventlet的参数,然后开启服务,最后等待所有线程执行完毕,即可返回

 greenio.shutdown_safe(sock)
    sock.close()
    logger.notice('Exited')
    return 0

最后退出操作,至此proxy-server顺利开启

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世纪殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值