def _log(self, data): if data: config = self.process.config if config.options.strip_ansi: data = stripEscapes(data) if self.childlog: self.childlog.info(data) if self.log_to_mainlog: if not isinstance(data, bytes): text = data else: try: text = data.decode('utf-8') except UnicodeDecodeError: text = 'Undecodable: %r' % data msg = '%(name)r %(channel)s output:\n%(data)s' config.options.logger.log( self.mainlog_level, msg, name=config.name, channel=self.channel, data=text) if self.channel == 'stdout': if self.stdout_events_enabled: notify( ProcessLogStdoutEvent(self.process, self.process.pid, data) ) else: # channel == stderr if self.stderr_events_enabled: notify( ProcessLogStderrEvent(self.process, self.process.pid, data) )
def remove_process_group(self, name): if self.process_groups[name].get_unstopped_processes(): return False self.process_groups[name].before_remove() del self.process_groups[name] events.notify(events.ProcessGroupRemovedEvent(name)) return True
def test_notify_true(self): from supervisor import events L = [] def callback(event): L.append(1) events.callbacks[:] = [(DummyEvent, callback)] events.notify(DummyEvent()) self.assertEqual(L, [1])
def add_process_group(self, config): name = config.name if name not in self.process_groups: config.after_setuid() self.process_groups[name] = config.make_group() events.notify(events.ProcessGroupAddedEvent(name)) return True return False
def test_notify_false(self): from supervisor import events L = [] def callback(event): L.append(1) class AnotherEvent: pass events.callbacks[:] = [(AnotherEvent, callback)] events.notify(DummyEvent()) self.assertEqual(L, [])
def test_notify_via_subclass(self): from supervisor import events L = [] def callback(event): L.append(1) class ASubclassEvent(DummyEvent): pass events.callbacks[:] = [(DummyEvent, callback)] events.notify(ASubclassEvent()) self.assertEqual(L, [1])
def change_state(self, new_state, expected=True): old_state = self.state if new_state is old_state: # exists for unit tests return False self.state = new_state if new_state == ProcessStates.BACKOFF: now = time.time() self.backoff += 1 self.delay = now + self.backoff event_class = self.event_map.get(new_state) if event_class is not None: event = event_class(self, old_state, expected) events.notify(event)
def sendRemoteCommEvent(self, type, data): """ Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent. @param string type String for the "type" key in the event header @param string data Data for the event body @return boolean Always return True unless error """ if isinstance(type, unicode): type = type.encode("utf-8") if isinstance(data, unicode): data = data.encode("utf-8") notify(RemoteCommunicationEvent(type, data)) return True
def test_notify_via_subclass(self): from supervisor import events L = [] def callback(event): L.append(1) class DummyEvent: pass class ASubclassEvent(DummyEvent): pass events.callbacks[:] = [(DummyEvent, callback)] events.notify(ASubclassEvent()) self.assertEqual(L, [1])
def tick(self, now=None): """ Send one or more 'tick' events when the timeslice related to the period for the event type rolls over """ if now is None: # now won't be None in unit tests now = time.time() for event in events.TICK_EVENTS: period = event.period last_tick = self.ticks.get(period) if last_tick is None: # we just started up last_tick = self.ticks[period] = timeslice(period, now) this_tick = timeslice(period, now) if this_tick != last_tick: self.ticks[period] = this_tick events.notify(event(this_tick, self))
def test_notify_false(self): from supervisor import events L = [] def callback(event): L.append(1) class DummyEvent: pass class AnotherEvent: pass events.callbacks[:] = [(AnotherEvent, callback)] events.notify(DummyEvent()) self.assertEqual(L, [])
def sendRemoteCommEvent(self, type, data): """ Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent. @param string type String for the "type" key in the event header @param string data Data for the event body @return boolean Always return True unless error """ if isinstance(type, unicode): type = type.encode('utf-8') if isinstance(data, unicode): data = data.encode('utf-8') notify(RemoteCommunicationEvent(type, data)) return True
def change_state(self, new_state, expected=True): old_state = self.state if new_state is old_state: # exists for unit tests return False event_class = self.event_map.get(new_state) if event_class is not None: event = event_class(self, old_state, expected) events.notify(event) if new_state == ProcessStates.BACKOFF: now = time.time() self.backoff = self.backoff + 1 self.delay = now + self.backoff self.state = new_state
def handle_result(self, result): process = self.process procname = process.config.name logger = process.config.options.logger try: self.process.group.config.result_handler(process.event, result) logger.debug('%s: event was processed' % procname) self._change_listener_state(EventListenerStates.ACKNOWLEDGED) except RejectEvent: logger.warn('%s: event was rejected' % procname) self._change_listener_state(EventListenerStates.ACKNOWLEDGED) notify(EventRejectedEvent(process, process.event)) except: logger.warn('%s: event caused an error' % procname) self._change_listener_state(EventListenerStates.UNKNOWN) notify(EventRejectedEvent(process, process.event))
def handle_result(self, result): process = self.process procname = process.config.name try: self.process.group.config.result_handler(process.event, result) msg = '%s: BUSY -> ACKNOWLEDGED (processed)' % procname process.listener_state = EventListenerStates.ACKNOWLEDGED except RejectEvent: msg = '%s: BUSY -> ACKNOWLEDGED (rejected)' % procname process.listener_state = EventListenerStates.ACKNOWLEDGED notify(EventRejectedEvent(process, process.event)) except: msg = '%s: BUSY -> UNKNOWN' % procname process.listener_state = EventListenerStates.UNKNOWN notify(EventRejectedEvent(process, process.event)) process.config.options.logger.debug(msg)
def sendRemoteCommEvent(self, type, data): """ Send an event that will be received by event listener subprocesses subscribing to the RemoteCommunicationEvent. :param type: String for the "type" key in the event header :type type: string :param data: Data for the event body :type data: string :return: Always return True unless error :rtype: boolean """ if isinstance(type, unicode): type = type.encode('utf-8') if isinstance(data, unicode): data = data.encode('utf-8') notify( RemoteCommunicationEvent(type, data) ) return True
def toggle_capturemode(self): self.capturemode = not self.capturemode if self.capturelog is not None: if self.capturemode: self.childlog = self.capturelog else: for handler in self.capturelog.handlers: handler.flush() data = self.capturelog.getvalue() channel = self.channel procname = self.process.config.name event = self.event_type(self.process, self.process.pid, data) notify(event) msg = "%(procname)r %(channel)s emitted a comm event" self.process.config.options.logger.debug(msg, procname=procname, channel=channel) self._remove_logs(self.capturelog) self.childlog = self.mainlog
def toggle_capturemode(self): self.capturemode = not self.capturemode if self.capturelog is not None: if self.capturemode: self.childlog = self.capturelog else: for handler in self.capturelog.handlers: handler.flush() data = self.capturelog.getvalue() channel = self.channel procname = self.process.config.name event = self.event_type(self.process, self.process.pid, data) notify(event) msg = "%(procname)r %(channel)s emitted a comm event" self.process.config.options.logger.debug(msg, procname=procname, channel=channel) for handler in self.capturelog.handlers: handler.remove() handler.reopen() self.childlog = self.mainlog
def _log(self, data): if data: config = self.process.config if config.options.strip_ansi: data = stripEscapes(data) if self.childlog: self.childlog.info(data) if self.log_to_mainlog: msg = '%(name)r %(channel)s output:\n%(data)s' config.options.logger.log(self.mainlog_level, msg, name=config.name, channel=self.channel, data=data) if self.channel == 'stdout': if self.stdout_events_enabled: notify( ProcessLogStdoutEvent(self.process, self.process.pid, data)) else: # channel == stderr if self.stderr_events_enabled: notify( ProcessLogStderrEvent(self.process, self.process.pid, data))
def _log(self, data): if data: config = self.process.config if config.options.strip_ansi: data = stripEscapes(data) if self.childlog: self.childlog.info(data) if self.log_to_mainlog: msg = '%(name)r %(channel)s output:\n%(data)s' config.options.logger.log( self.mainlog_level, msg, name=config.name, channel=self.channel, data=data) if self.channel == 'stdout': if self.stdout_events_enabled: notify( ProcessLogStdoutEvent(self.process, self.process.pid, data) ) else: # channel == stderr if self.stderr_events_enabled: notify( ProcessLogStderrEvent(self.process, self.process.pid, data) )
def runforever(self): events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) socket_map = self.options.get_socket_map() while 1: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = self.process_groups.values() pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to swtop or reload raise asyncore.ExitNow r, w, x = [], [], [] for fd, dispatcher in combined_map.items(): if dispatcher.readable(): r.append(fd) if dispatcher.writable(): w.append(fd) try: r, w, x = self.options.select(r, w, x, timeout) except select.error, err: r = w = x = [] if err.args[0] == errno.EINTR: self.options.logger.blather('EINTR encountered in select') else: raise for fd in r: if combined_map.has_key(fd): try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_read_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in w: if combined_map.has_key(fd): try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_write_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() [group.transition() for group in pgroups] self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break
def finish(self, pid, sts): """ The process was reaped and we need to report and manage its state """ self.drain() es, msg = decode_wait_status(sts) now = time.time() self.laststop = now processname = self.config.name tooquickly = now - self.laststart < self.config.startsecs exit_expected = es in self.config.exitcodes if self.killing: # likely the result of a stop request # implies STOPPING -> STOPPED self.killing = 0 self.delay = 0 self.exitstatus = es msg = "stopped: %s (%s)" % (processname, msg) self._assertInState(ProcessStates.STOPPING) self.change_state(ProcessStates.STOPPED) elif tooquickly: # the program did not stay up long enough to make it to RUNNING # implies STARTING -> BACKOFF self.exitstatus = None self.spawnerr = 'Exited too quickly (process log may have details)' msg = "exited: %s (%s)" % (processname, msg + "; not expected") self._assertInState(ProcessStates.STARTING) self.change_state(ProcessStates.BACKOFF) else: # this finish was not the result of a stop request, the # program was in the RUNNING state but exited implies # RUNNING -> EXITED self.delay = 0 self.backoff = 0 self.exitstatus = es if self.state == ProcessStates.STARTING: # XXX I dont know under which circumstances this # happens, but in the wild, there is a transition that # subverts the RUNNING state (directly from STARTING # to EXITED), so we perform the correct transition # here. self.change_state(ProcessStates.RUNNING) self._assertInState(ProcessStates.RUNNING) if exit_expected: # expected exit code msg = "exited: %s (%s)" % (processname, msg + "; expected") self.change_state(ProcessStates.EXITED, expected=True) else: # unexpected exit code self.spawnerr = 'Bad exit code %s' % es msg = "exited: %s (%s)" % (processname, msg + "; not expected") self.change_state(ProcessStates.EXITED, expected=False) self.config.options.logger.info(msg) self.pid = 0 self.config.options.close_parent_pipes(self.pipes) self.pipes = {} self.dispatchers = {} # if we died before we processed the current event (only happens # if we're an event listener), notify the event system that this # event was rejected so it can be processed again. if self.event is not None: # Note: this should only be true if we were in the BUSY # state when finish() was called. events.notify(events.EventRejectedEvent(self, self.event)) self.event = None
def runforever(self): events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) socket_map = self.options.get_socket_map() while 1: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = list(self.process_groups.values()) pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to shutdown or reload raise asyncore.ExitNow for fd, dispatcher in combined_map.items(): if dispatcher.readable(): self.options.poller.register_readable(fd) if dispatcher.writable(): self.options.poller.register_writable(fd) r, w = self.options.poller.poll(timeout) for fd in r: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_read_event() if not dispatcher.readable(): self.options.poller.unregister_readable(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in w: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_write_event() if not dispatcher.writable(): self.options.poller.unregister_writable(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for group in pgroups: group.transition() self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break
def runforever(self): events.notify(events.SupervisorRunningEvent()) socket_map = self.options.get_socket_map() # this cannot be fewer than the smallest TickEvent (5) poller_timeout = 1.0 while True: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = list(self.process_groups.values()) pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to swtop or reload raise asyncore.ExitNow for fd, dispatcher in combined_map.items(): if dispatcher.readable(): if hasattr(dispatcher, "socket"): self.options.poller.register_readable( dispatcher.socket) else: self.dispatcher_handle_event(dispatcher, "handle_read_event") if dispatcher.writable(): if hasattr(dispatcher, "socket"): self.options.poller.register_writable( dispatcher.socket) else: self.dispatcher_handle_event(dispatcher, "handle_write_event") readables, writables = self.options.poller.poll(poller_timeout) for fd in readables: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_read_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in writables: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_write_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for group in pgroups: group.transition() self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break try: # Avoid overloading the processor time.sleep(self.options.delay_secs) except IOError: continue
def remove_process_group(self, name): if self.process_groups[name].get_unstopped_processes(): return False del self.process_groups[name] events.notify(events.ProcessGroupRemovedEvent(name)) return True
def runforever(self): events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) # 网络 socket io socket_map = self.options.get_socket_map() while 1: combined_map = {} # 合并句柄 combined_map.update(socket_map) # socket io combined_map.update(self.get_process_map()) # process 管道 # 管道比较特殊,只能单向传输,要读就必须关闭写操作,要写就必须关闭读操作 # 子进程 pgroups = list(self.process_groups.values()) pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to shutdown or reload raise asyncore.ExitNow # 注册事件监听 for fd, dispatcher in combined_map.items(): if dispatcher.readable(): self.options.poller.register_readable(fd) if dispatcher.writable(): self.options.poller.register_writable(fd) # 获取事件监听结果集 并 处理监听事件 r, w = self.options.poller.poll(timeout) for fd in r: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_read_event() if not dispatcher.readable(): # 管道特殊处理 self.options.poller.unregister_readable(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in w: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_write_event() if not dispatcher.writable(): # 管道特殊处理 self.options.poller.unregister_writable(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() # 子进程状态检测(本身进程的当前状态和目标状态的匹配,状态转换) for group in pgroups: # 判断配置子进程的状态,来决定该子进程是否运行(这其中是由于有些进程可以配置延迟执行) # 通过调用子进程实例的spwn()方法来运行子进程 group.transition() # 清理死进程以及相关维护信息 self.reap() # 信号处理 self.handle_signal() # 发送标记信号 self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break
def handle_listener_state_change(self): data = self.state_buffer if not data: return process = self.process procname = process.config.name state = process.listener_state if state == EventListenerStates.UNKNOWN: # this is a fatal state self.state_buffer = '' return if state == EventListenerStates.ACKNOWLEDGED: if len(data) < self.READY_FOR_EVENTS_LEN: # not enough info to make a decision return elif data.startswith(self.READY_FOR_EVENTS_TOKEN): msg = '%s: ACKNOWLEDGED -> READY' % procname process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.READY tokenlen = self.READY_FOR_EVENTS_LEN self.state_buffer = self.state_buffer[tokenlen:] process.event = None else: msg = '%s: ACKNOWLEDGED -> UNKNOWN' % procname process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.UNKNOWN self.state_buffer = '' process.event = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return elif state == EventListenerStates.READY: # the process sent some spurious data, be a hardass about it msg = '%s: READY -> UNKNOWN' % procname process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.UNKNOWN self.state_buffer = '' process.event = None return elif state == EventListenerStates.BUSY: if self.resultlen is None: # we haven't begun gathering result data yet pos = data.find('\n') if pos == -1: # we can't make a determination yet, we dont have a full # results line return result_line = self.state_buffer[:pos] self.state_buffer = self.state_buffer[pos+1:] # rid LF resultlen = result_line[self.RESULT_TOKEN_START_LEN:] try: self.resultlen = int(resultlen) except ValueError: msg = ('%s: BUSY -> UNKNOWN (bad result line %r)' % (procname, result_line)) process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.UNKNOWN self.state_buffer = '' notify(EventRejectedEvent(process, process.event)) process.event = None return else: needed = self.resultlen - len(self.result) if needed: self.result += self.state_buffer[:needed] self.state_buffer = self.state_buffer[needed:] needed = self.resultlen - len(self.result) if not needed: self.handle_result(self.result) self.process.event = None self.result = '' self.resultlen = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return
def runforever(self): events.notify(events.SupervisorRunningEvent()) socket_map = self.options.get_socket_map() # this cannot be fewer than the smallest TickEvent (5) poller_timeout = 1.0 while True: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = list(self.process_groups.values()) pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to swtop or reload raise asyncore.ExitNow for fd, dispatcher in combined_map.items(): if dispatcher.readable(): if hasattr(dispatcher, "socket"): self.options.poller.register_readable(dispatcher.socket) else: self.dispatcher_handle_event(dispatcher, "handle_read_event") if dispatcher.writable(): if hasattr(dispatcher, "socket"): self.options.poller.register_writable(dispatcher.socket) else: self.dispatcher_handle_event(dispatcher, "handle_write_event") readables, writables = self.options.poller.poll(poller_timeout) for fd in readables: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_read_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in writables: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_write_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for group in pgroups: group.transition() self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break try: # Avoid overloading the processor time.sleep(self.options.delay_secs) except IOError: continue
def handle_listener_state_change(self): data = self.state_buffer if not data: return process = self.process procname = process.config.name state = process.listener_state if state == EventListenerStates.UNKNOWN: # this is a fatal state self.state_buffer = '' return if state == EventListenerStates.ACKNOWLEDGED: if len(data) < self.READY_FOR_EVENTS_LEN: # not enough info to make a decision return elif data.startswith(self.READY_FOR_EVENTS_TOKEN): msg = '%s: ACKNOWLEDGED -> READY' % procname process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.READY tokenlen = self.READY_FOR_EVENTS_LEN self.state_buffer = self.state_buffer[tokenlen:] process.event = None else: msg = '%s: ACKNOWLEDGED -> UNKNOWN' % procname process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.UNKNOWN self.state_buffer = '' process.event = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return elif state == EventListenerStates.READY: # the process sent some spurious data, be a hardass about it msg = '%s: READY -> UNKNOWN' % procname process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.UNKNOWN self.state_buffer = '' process.event = None return elif state == EventListenerStates.BUSY: if self.resultlen is None: # we haven't begun gathering result data yet pos = data.find('\n') if pos == -1: # we can't make a determination yet, we dont have a full # results line return result_line = self.state_buffer[:pos] self.state_buffer = self.state_buffer[pos + 1:] # rid LF resultlen = result_line[self.RESULT_TOKEN_START_LEN:] try: self.resultlen = int(resultlen) except ValueError: msg = ('%s: BUSY -> UNKNOWN (bad result line %r)' % (procname, result_line)) process.config.options.logger.debug(msg) process.listener_state = EventListenerStates.UNKNOWN self.state_buffer = '' notify(EventRejectedEvent(process, process.event)) process.event = None return else: needed = self.resultlen - len(self.result) if needed: self.result += self.state_buffer[:needed] self.state_buffer = self.state_buffer[needed:] needed = self.resultlen - len(self.result) if not needed: self.handle_result(self.result) self.process.event = None self.result = '' self.resultlen = None if self.state_buffer: # keep going til its too short self.handle_listener_state_change() else: return
def finish(self, pid, sts): """ The process was reaped and we need to report and manage its state """ self.drain() es, msg = decode_wait_status(sts) now = time.time() self.laststop = now processname = self.config.name if now > self.laststart: too_quickly = now - self.laststart < self.config.startsecs else: too_quickly = False self.config.options.logger.warn( "process %r (%s) laststart time is in the future, don't " "know how long process was running so assuming it did " "not exit too quickly" % (self.config.name, self.pid)) exit_expected = es in self.config.exitcodes if self.killing: # likely the result of a stop request # implies STOPPING -> STOPPED self.killing = False self.delay = 0 self.exitstatus = es msg = "stopped: %s (%s)" % (processname, msg) self._assertInState(ProcessStates.STOPPING) self.change_state(ProcessStates.STOPPED) elif too_quickly: # the program did not stay up long enough to make it to RUNNING # implies STARTING -> BACKOFF self.exitstatus = None self.spawnerr = 'Exited too quickly (process log may have details)' msg = "exited: %s (%s)" % (processname, msg + "; not expected") self._assertInState(ProcessStates.STARTING) self.change_state(ProcessStates.BACKOFF) else: # this finish was not the result of a stop request, the # program was in the RUNNING state but exited # implies RUNNING -> EXITED normally but see next comment self.delay = 0 self.backoff = 0 self.exitstatus = es # if the process was STARTING but a system time change causes # self.laststart to be in the future, the normal STARTING->RUNNING # transition can be subverted so we perform the transition here. if self.state == ProcessStates.STARTING: self.change_state(ProcessStates.RUNNING) self._assertInState(ProcessStates.RUNNING) if exit_expected: # expected exit code msg = "exited: %s (%s)" % (processname, msg + "; expected") self.change_state(ProcessStates.EXITED, expected=True) else: # unexpected exit code self.spawnerr = 'Bad exit code %s' % es msg = "exited: %s (%s)" % (processname, msg + "; not expected") self.change_state(ProcessStates.EXITED, expected=False) self.config.options.logger.info(msg) self.pid = 0 self.config.options.close_parent_pipes(self.pipes) self.pipes = {} self.dispatchers = {} # if we died before we processed the current event (only happens # if we're an event listener), notify the event system that this # event was rejected so it can be processed again. if self.event is not None: # Note: this should only be true if we were in the BUSY # state when finish() was called. events.notify(events.EventRejectedEvent(self, self.event)) self.event = None
def runforever(self): events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) socket_map = self.options.get_socket_map() while 1: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = self.process_groups.values() pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to swtop or reload raise asyncore.ExitNow r, w, x = [], [], [] for fd, dispatcher in combined_map.items(): if dispatcher.readable(): r.append(fd) if dispatcher.writable(): w.append(fd) try: r, w, x = self.options.select(r, w, x, timeout) except select.error, err: r = w = x = [] if err.args[0] == errno.EINTR: self.options.logger.blather('EINTR encountered in select') else: raise for fd in r: if combined_map.has_key(fd): try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_read_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in w: if combined_map.has_key(fd): try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)s', dispatcher=dispatcher) dispatcher.handle_write_event() except asyncore.ExitNow: raise except: combined_map[fd].handle_error() [ group.transition() for group in pgroups ] self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break
def runforever(self): events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) socket_map = self.options.get_socket_map() while 1: combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) pgroups = list(self.process_groups.values()) pgroups.sort() if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to swtop or reload raise asyncore.ExitNow for fd, dispatcher in combined_map.items(): if dispatcher.readable(): self.options.poller.register_readable(fd) if dispatcher.writable(): self.options.poller.register_writable(fd) r, w = self.options.poller.poll(timeout) for fd in r: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'read event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_read_event() if (not dispatcher.readable() and not dispatcher.writable()): self.options.poller.unregister(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for fd in w: if fd in combined_map: try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_write_event() if (not dispatcher.readable() and not dispatcher.writable()): self.options.poller.unregister(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() [ group.transition() for group in pgroups ] self.reap() self.handle_signal() self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break
def finish(self, pid, sts): """ The process was reaped and we need to report and manage its state """ self.drain() es, msg = decode_wait_status(sts) now = time.time() self.laststop = now processname = as_string(self.config.name) if now > self.laststart: too_quickly = now - self.laststart < self.config.startsecs else: too_quickly = False self.config.options.logger.warn( "process \'%s\' (%s) laststart time is in the future, don't " "know how long process was running so assuming it did " "not exit too quickly" % (processname, self.pid)) exit_expected = es in self.config.exitcodes if self.killing: # likely the result of a stop request # implies STOPPING -> STOPPED self.killing = False self.delay = 0 self.exitstatus = es msg = "stopped: %s (%s)" % (processname, msg) self._assertInState(ProcessStates.STOPPING) self.change_state(ProcessStates.STOPPED) elif too_quickly: # the program did not stay up long enough to make it to RUNNING # implies STARTING -> BACKOFF self.exitstatus = None self.spawnerr = 'Exited too quickly (process log may have details)' msg = "exited: %s (%s)" % (processname, msg + "; not expected") self._assertInState(ProcessStates.STARTING) self.change_state(ProcessStates.BACKOFF) else: # this finish was not the result of a stop request, the # program was in the RUNNING state but exited # implies RUNNING -> EXITED normally but see next comment self.delay = 0 self.backoff = 0 self.exitstatus = es # if the process was STARTING but a system time change causes # self.laststart to be in the future, the normal STARTING->RUNNING # transition can be subverted so we perform the transition here. if self.state == ProcessStates.STARTING: self.change_state(ProcessStates.RUNNING) self._assertInState(ProcessStates.RUNNING) if exit_expected: # expected exit code msg = "exited: %s (%s)" % (processname, msg + "; expected") self.change_state(ProcessStates.EXITED, expected=True) else: # unexpected exit code self.spawnerr = 'Bad exit code %s' % es msg = "exited: %s (%s)" % (processname, msg + "; not expected") self.change_state(ProcessStates.EXITED, expected=False) self.config.options.logger.info(msg) self.pid = 0 self.config.options.close_parent_pipes(self.pipes) self.pipes = {} self.dispatchers = {} # if we died before we processed the current event (only happens # if we're an event listener), notify the event system that this # event was rejected so it can be processed again. if self.event is not None: # Note: this should only be true if we were in the BUSY # state when finish() was called. events.notify(events.EventRejectedEvent(self, self.event)) self.event = None
def runforever(self): # 事件通知机制,使用callbacks中的方法对event实例进行操作 # callbacks一直是空的,现在还不知道怎么添加方法到其中 events.notify(events.SupervisorRunningEvent()) timeout = 1 # this cannot be fewer than the smallest TickEvent (5) # 获得一件注册的句柄 {4: <supervisor.http.supervisor_af_unix_http_server at 0x7f8b5fcb0488>} 不知道在哪里设置的 # supervisor_af_unix_http_server对象 socket_map = self.options.get_socket_map() # print('socket_map:',socket_map) while 1: # 保存运行的信息 combined_map = {} combined_map.update(socket_map) combined_map.update(self.get_process_map()) # 进程信息 socket_map我追溯了很长时间,类之间关联太复杂了,放弃治疗 # 更新,窝没有放弃治疗,我找到了 # self.get_process_map()为空 pgroups = list(self.process_groups.values()) # 这个排序是按照内存地址进行的吗 # 这个排序是processgroup中定义的,按照配置中的优先级进行排序 # def __lt__(self, other): # return self.config.priority < other.config.priority pgroups.sort() # 根据进程的配置开启或者关闭进程 # self.options.mood 在信号中变化, RESTARTING = 0 SHUTDOWN = -1 这两个是小于 if self.options.mood < SupervisorStates.RUNNING: if not self.stopping: # first time, set the stopping flag, do a # notification and set stop_groups self.stopping = True self.stop_groups = pgroups[:] events.notify(events.SupervisorStoppingEvent()) self.ordered_stop_groups_phase_1() if not self.shutdown_report(): # if there are no unstopped processes (we're done # killing everything), it's OK to shutdown or reload raise asyncore.ExitNow # 这个和我们用redis差不多来着 for fd, dispatcher in combined_map.items(): # class http_server (asyncore.dispatcher): 在右键第四个 if dispatcher.readable(): #可读,注册到select,证明这个句柄是好的 self.options.poller.register_readable(fd) if dispatcher.writable(): # 初始时候不可写,注册到select self.options.poller.register_writable(fd) # poll操作,返回可读可写的文件描述符,到这一步完全没有问题 r, w = self.options.poller.poll(timeout) # print(combined_map) # for i,key in combined_map.items(): # print(key) for fd in r: if fd in combined_map: # print('r start running',fd) try: dispatcher = combined_map[fd] # print(dispatcher) self.options.logger.blather( 'read event caused by %(dispatcher)r', dispatcher=dispatcher) # print('ttttt') # print(dispatcher) # <socket._socketobject object at 0x7fdfa69b96e0> # print(dispatcher.__class__.__name__) # dispatcher.test_handler() dispatcher.handle_read_event() if not dispatcher.readable(): self.options.poller.unregister_readable(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() # 依次遍历注册的文件句柄 for fd in w: if fd in combined_map: # print('w start running', fd) try: dispatcher = combined_map[fd] self.options.logger.blather( 'write event caused by %(dispatcher)r', dispatcher=dispatcher) dispatcher.handle_write_event() if not dispatcher.writable(): self.options.poller.unregister_writable(fd) except asyncore.ExitNow: raise except: combined_map[fd].handle_error() for group in pgroups: group.transition() # 获取已经死亡的子进程信息 self.reap() # 处理信号 self.handle_signal() # tick时钟 self.tick() if self.options.mood < SupervisorStates.RUNNING: self.ordered_stop_groups_phase_2() if self.options.test: break # 新加,测试用 # break '''