cotyledon学习小结

1 cotyledon._service_manager.ServiceManager

1.1 add()

service_id = uuid.uuid4()
self._services[service_id] = _service.ServiceConfig(service_id, service, workers, args, kwargs)

1.2 run()

def run(self):
    """Start and supervise services workers

    This method will start and supervise all children processes
    until the master process asked to shutdown by a SIGTERM.

    All spawned processes are part of the same unix process group.
    """
    # 删NOTIFY_SOCKET,确保只通知一次
    self._systemd_notify_once()
    self._child_supervisor = _utils.spawn(self._child_supervisor_thread)
    self._wait_forever()

1.3 register_hooks()

    if on_terminate is not None:
        _utils.check_callable(on_terminate, 'on_terminate')
        self._hooks['terminate'].append(on_terminate)
    if on_reload is not None:
        _utils.check_callable(on_reload, 'on_reload')
        self._hooks['reload'].append(on_reload)
    if on_new_worker is not None:
        _utils.check_callable(on_new_worker, 'on_new_worker')
        self._hooks['new_worker'].append(on_new_worker)
    if on_dead_worker is not None:
        _utils.check_callable(on_dead_worker, 'on_dead_worker')
        self._hooks['dead_worker'].append(on_dead_worker)

1.4 _child_supervisor_thread

def _child_supervisor_thread(self):
    while not self._dead.is_set():
        self._got_sig_chld.wait()
        self._got_sig_chld.clear()

        info = self._get_last_worker_died()
        while info is not None:
            if self._dead.is_set():
                return
            service_id, worker_id = info
            self._start_worker(service_id, worker_id)
            info = self._get_last_worker_died()

        self._adjust_workers()

1.5 _get_last_worker_died

    for service_id in list(self._running_services.keys()):
        # We copy the list to clean the orignal one
        processes = list(self._running_services[service_id].items())
        for process, worker_id in processes:
            if not process.is_alive():
                self._run_hooks('dead_worker', service_id, worker_id,
                                process.exitcode)
                if process.exitcode < 0:
                    sig = _utils.signal_to_name(process.exitcode)
                    LOG.info('Child %(pid)d killed by signal %(sig)s',
                             dict(pid=process.pid, sig=sig))
                else:
                    LOG.info('Child %(pid)d exited with status %(code)d',
                             dict(pid=process.pid, code=process.exitcode))
                del self._running_services[service_id][process]
                return service_id, worker_id

1.6 init

def __init__(self, wait_interval=0.01, graceful_shutdown_timeout=60):
    """Creates the ServiceManager object

    :param wait_interval: time between each new process spawn
    :type wait_interval: float

    """

    if self._process_runner_already_created:
        raise RuntimeError("Only one instance of ServiceManager per "
                           "application is allowed")
    ServiceManager._process_runner_already_created = True
    super(ServiceManager, self).__init__()

    # We use OrderedDict to start services in adding order
    self._services = collections.OrderedDict()
    self._running_services = collections.defaultdict(dict)
    self._forktimes = []
    self._graceful_shutdown_timeout = graceful_shutdown_timeout
    self._wait_interval = wait_interval

    self._dead = threading.Event()
    # NOTE(sileht): Set it on startup, so first iteration
    # will spawn initial workers
    self._got_sig_chld = threading.Event()
    self._got_sig_chld.set()

    self._child_supervisor = None

    self._hooks = {
        'terminate': [],
        'reload': [],
        'new_worker': [],
        'dead_worker': [],
    }

    _utils.setproctitle("%s: master process [%s]" %
                        (_utils.get_process_name(), " ".join(sys.argv)))

    # Try to create a session id if possible
    try:
        os.setsid()
    except (OSError, AttributeError):
        pass

    self._death_detection_pipe = multiprocessing.Pipe(duplex=False)

    signal.signal(signal.SIGINT, self._fast_exit)
    if os.name == 'posix':
        signal.signal(signal.SIGCHLD, self._signal_catcher)

1.7 _adjust_worker()

def _adjust_workers(self):
    for service_id, conf in self._services.items():
        running_workers = len(self._running_services[service_id])
        if running_workers < conf.workers:
            for worker_id in range(running_workers, conf.workers):
                self._start_worker(service_id, worker_id)
        elif running_workers > conf.workers:
            for worker_id in range(running_workers, conf.workers):
                self._stop_worker(service_id, worker_id)

1.8 _start_worker()

def _start_worker(self, service_id, worker_id):
    self._slowdown_respawn_if_needed()

    if os.name == "posix":
        fds = [self.signal_pipe_w, self.signal_pipe_r]
    else:
        fds = []

    # Create and run a new service
    p = _utils.spawn_process(
        _service.ServiceWorker.create_and_wait,
        self._services[service_id],
        service_id,
        worker_id,
        self._death_detection_pipe,
        self._hooks['new_worker'],
        self._graceful_shutdown_timeout,
        fds_to_close=fds)

    self._running_services[service_id][p] = worker_id

2 ServiceConfig

2.1 init

class ServiceConfig(object):
    def __init__(self, service_id, service, workers, args, kwargs):
        self.service = service
        self.workers = workers
        self.args = args
        self.kwargs = kwargs
        self.service_id = service_id

3 cotyledon.oslo_config_glue

3.1 setup

主要功能:给servicemanager各阶段注册回调函数

# 加载log_options和graceful_shutdown_time
conf.register_opts(service_opts)

# Set cotyledon options from oslo config options
# 将上述加载两个配置项赋值给service_manager
_load_service_manager_options(service_manager, conf)

def _service_manager_reload():
    # 重新加载配置文件
    _configfile_reload(conf, reload_method)
    _load_service_manager_options(service_manager, conf)

if os.name != "posix":
    # NOTE(sileht): reloading can't be supported oslo.config is not pickle
    # But we don't care SIGHUP is not support on window
    return

# 给创worker和reload注册回调函数
service_manager.register_hooks(
    on_new_worker=functools.partial(
        _new_worker_hook, conf, reload_method),
    on_reload=_service_manager_reload)

4 cotyledon._utils.SignalManager

4.1 _wait_forever

def _wait_forever(self):
    # Wait forever
    while True:
        # Check if signals have been received
        if os.name == "posix":
            self._empty_signal_pipe()
        self._run_signal_handlers()

        if os.name == "posix":
            # NOTE(sileht): we cannot use threading.Event().wait(),
            # threading.Thread().join(), or time.sleep() because signals
            # can be missed when received by non-main threads
            # (https://bugs.python.org/issue5315)
            # So we use select.select() alone, we will receive EINTR or
            # will read data from signal_r when signal is emitted and
            # cpython calls PyErr_CheckSignals() to run signals handlers
            # That looks perfect to ensure handlers are run and run in the
            # main thread
            try:
                select.select([self.signal_pipe_r], [], [])
            except select.error as e:
                if e.args[0] != errno.EINTR:
                    raise
        else:
            # NOTE(sileht): here we do only best effort
            # and wake the loop periodically, set_wakeup_fd
            # doesn't work on non posix platform so
            # 1 seconds have been picked with the advice of a dice.
            time.sleep(1)
            # NOTE(sileht): We emulate SIGCHLD, _service_manager
            # will just check often for dead child
            self._signals_received.append(SIGCHLD)

4.2 init

def __init__(self):
    # Setup signal fd, this allows signal to behave correctly
    if os.name == 'posix':
        self.signal_pipe_r, self.signal_pipe_w = os.pipe()
        self._set_nonblock(self.signal_pipe_r)
        self._set_nonblock(self.signal_pipe_w)
        signal.set_wakeup_fd(self.signal_pipe_w)

    self._signals_received = collections.deque()

    signal.signal(signal.SIGINT, signal.SIG_DFL)
    if os.name == 'posix':
        signal.signal(signal.SIGCHLD, signal.SIG_DFL)
        signal.signal(signal.SIGTERM, self._signal_catcher)
        signal.signal(signal.SIGALRM, self._signal_catcher)
        signal.signal(signal.SIGHUP, self._signal_catcher)
    else:
        # currently a noop on window...
        signal.signal(signal.SIGTERM, self._signal_catcher)
        # FIXME(sileht): should allow to catch signal CTRL_BREAK_EVENT,
        # but we to create the child process with CREATE_NEW_PROCESS_GROUP
        # to make this work, so current this is a noop for later fix
        signal.signal(signal.SIGBREAK, self._signal_catcher)

分类:openstack

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值