def _run_multiple_mode(self, workers: int, *args, **kwargs): import multiprocessing log.debug(self.config.driver.logo) processes = [] def sig_handler(signal, frame): log.info("Received signal %s. Shutting down.", Signals(signal).name) for process in processes: os.kill(process.pid, SIGTERM) signal_func(SIGINT, lambda s, f: sig_handler(s, f)) signal_func(SIGTERM, lambda s, f: sig_handler(s, f)) for _ in range(workers): p = multiprocessing.Process(target=self._run_single_mode, daemon=True, kwargs={ 'run_multiple': True, }) p.start() processes.append(p) for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate()
def serve_multiple(server_settings, workers): """Start multiple server processes simultaneously. Stop on interrupt and terminate signals, and drain connections when complete. :param server_settings: kw arguments to be passed to the serve function :param workers: number of workers to launch :param stop_event: if provided, is used as a stop signal :return: """ server_settings["reuse_port"] = True server_settings["run_multiple"] = True # Create a listening socket or use the one in settings sock = server_settings.get("sock") unix = server_settings["unix"] backlog = server_settings["backlog"] if unix: sock = bind_unix_socket(unix, backlog=backlog) server_settings["unix"] = unix if sock is None: sock = bind_socket(server_settings["host"], server_settings["port"], backlog=backlog) sock.set_inheritable(True) server_settings["sock"] = sock server_settings["host"] = None server_settings["port"] = None processes = [] def sig_handler(signal, frame): logger.info("Received signal %s. Shutting down.", Signals(signal).name) for process in processes: os.kill(process.pid, SIGTERM) signal_func(SIGINT, lambda s, f: sig_handler(s, f)) signal_func(SIGTERM, lambda s, f: sig_handler(s, f)) mp = multiprocessing.get_context("fork") for _ in range(workers): process = mp.Process(target=serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate() sock.close() remove_unix_socket(unix)
def serve_multiple(server_settings, workers): """Start multiple server processes simultaneously. Stop on interrupt and terminate signals, and drain connections when complete. :param server_settings: kw arguments to be passed to the serve function :param workers: number of workers to launch :param stop_event: if provided, is used as a stop signal :return: """ if server_settings.get('loop', None) is not None: if server_settings.get('debug', False): warnings.simplefilter('default') warnings.warn( "Passing a loop will be deprecated in version 0.4.0" " https://github.com/channelcat/sanic/pull/335" " has more information.", DeprecationWarning) server_settings['reuse_port'] = True # Handling when custom socket is not provided. if server_settings.get('sock') is None: sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind((server_settings['host'], server_settings['port'])) sock.set_inheritable(True) server_settings['sock'] = sock server_settings['host'] = None server_settings['port'] = None def sig_handler(signal, frame): log.info("Received signal {}. Shutting down.".format( Signals(signal).name)) for process in processes: os.kill(process.pid, SIGINT) signal_func(SIGINT, lambda s, f: sig_handler(s, f)) signal_func(SIGTERM, lambda s, f: sig_handler(s, f)) processes = [] for _ in range(workers): process = Process(target=serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate() server_settings.get('sock').close() asyncio.get_event_loop().stop()
def _run_single_mode(self, run_multiple: bool = False, *args, **kwargs): uvloop.install() pid = os.getpid() log.info(f'<Aiopika worker: {pid}> Starting worker') await_func = self.loop.run_until_complete if self.config.driver.sentry_dsn: sentry_sdk.init(dsn=self.config.driver.sentry_dsn, environment=self.config.driver.sentry_env) await_func(self._call_hooks( AiopikaHookNames.before_server_start.value)) connection: Connection = None try: connection = await_func(self._consume()) # Ignore SIGINT when run_multiple if run_multiple: signal_func(SIGINT, SIG_IGN) # Register signals for graceful termination _singals = (SIGTERM, ) if run_multiple else (SIGINT, SIGTERM) for _signal in _singals: try: def fu(): print('stop') self.loop.stop() self.loop.add_signal_handler(_signal, self.loop.stop) except NotImplementedError: log.warning( "AiopikaDriver tried to use loop.add_signal_handler " "but it is not implemented on this platform.") self.loop.run_forever() except BaseException as e: log.error(e) if self._connection is not None: await_func(connection.close()) finally: if self._connection is not None: await_func(self._connection.close()) log.info(f'<Aiopika worker: {pid}> Stopping worker') await_func( self._call_hooks(AiopikaHookNames.after_server_stop.value)) self.loop.close()
def serve_multiple(server_settings, workers): """Start multiple server processes simultaneously. Stop on interrupt and terminate signals, and drain connections when complete. :param server_settings: kw arguments to be passed to the serve function :param workers: number of workers to launch :param stop_event: if provided, is used as a stop signal :return: """ server_settings["reuse_port"] = True server_settings["run_multiple"] = True # Handling when custom socket is not provided. if server_settings.get("sock") is None: sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind((server_settings["host"], server_settings["port"])) sock.set_inheritable(True) server_settings["sock"] = sock server_settings["host"] = None server_settings["port"] = None processes = [] def sig_handler(signal, frame): logger.info("Received signal %s. Shutting down.", Signals(signal).name) for process in processes: os.kill(process.pid, SIGTERM) signal_func(SIGINT, lambda s, f: sig_handler(s, f)) signal_func(SIGTERM, lambda s, f: sig_handler(s, f)) mp = multiprocessing.get_context("fork") # 启动多个子进程 for _ in range(workers): process = mp.Process(target=serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) # 等待所有子进程结束 for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate() server_settings.get("sock").close()
def serve_multiple(server_settings, workers, stop_event=None): """ Starts multiple server processes simultaneously. Stops on interrupt and terminate signals, and drains connections when complete. :param server_settings: kw arguments to be passed to the serve function :param workers: number of workers to launch :param stop_event: if provided, is used as a stop signal :return: """ if server_settings.get('loop', None) is not None: if server_settings.get('debug', False): warnings.simplefilter('default') warnings.warn("Passing a loop will be deprecated in version 0.4.0" " https://github.com/channelcat/sanic/pull/335" " has more information.", DeprecationWarning) server_settings['reuse_port'] = True sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind((server_settings['host'], server_settings['port'])) set_inheritable(sock.fileno(), True) server_settings['sock'] = sock server_settings['host'] = None server_settings['port'] = None if stop_event is None: stop_event = Event() signal_func(SIGINT, lambda s, f: stop_event.set()) signal_func(SIGTERM, lambda s, f: stop_event.set()) processes = [] for _ in range(workers): process = Process(target=serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate() sock.close() asyncio.get_event_loop().stop()
def serve_multiple(server_settings, workers): """Start multiple server processes simultaneously. Stop on interrupt and terminate signals, and drain connections when complete. :param server_settings: kw arguments to be passed to the serve function :param workers: number of workers to launch :param stop_event: if provided, is used as a stop signal :return: """ server_settings["reuse_port"] = True server_settings["run_multiple"] = True # Handling when custom socket is not provided. if server_settings.get("sock") is None: sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind((server_settings["host"], server_settings["port"])) sock.set_inheritable(True) server_settings["sock"] = sock server_settings["host"] = None server_settings["port"] = None def sig_handler(signal, frame): logger.info("Received signal %s. Shutting down.", Signals(signal).name) for process in processes: os.kill(process.pid, SIGTERM) signal_func(SIGINT, lambda s, f: sig_handler(s, f)) signal_func(SIGTERM, lambda s, f: sig_handler(s, f)) processes = [] for _ in range(workers): process = Process(target=serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate() server_settings.get("sock").close()
def serve_multiple(self, server_settings, workers): server_settings['reuse_port'] = True # Handling when custom socket is not provided. if server_settings.get('sock') is None: sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind((server_settings['host'], server_settings['port'])) sock.set_inheritable(True) server_settings['sock'] = sock server_settings['host'] = None server_settings['port'] = None log = server_settings['log'] def sig_handler(signal, frame): log.info("Received signal {}. Shutting down.".format( Signals(signal).name)) for process in processes: os.kill(process.pid, SIGINT) signal_func(SIGINT, lambda s, f: sig_handler(s, f)) signal_func(SIGTERM, lambda s, f: sig_handler(s, f)) processes = [] for _ in range(workers): process = Process(target=self.serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) for process in processes: process.join() # the above processes will block this until they're stopped for process in processes: process.terminate() server_settings.get('sock').close()
def serve(host, port, request_handler, error_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, response_timeout=60, keep_alive_timeout=5, ssl=None, sock=None, request_max_size=None, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, register_sys_signals=True, run_multiple=False, run_async=False, connections=None, signal=Signal(), request_class=None, access_log=True, keep_alive=True, is_request_stream=False, router=None, websocket_max_size=None, websocket_max_queue=None, websocket_read_limit=2**16, websocket_write_limit=2**16, state=None, graceful_shutdown_timeout=15.0): """Start asynchronous HTTP Server on an individual process. :param host: Address to host on :param port: Port to host on :param request_handler: Sanic request handler with middleware :param error_handler: Sanic error handler with middleware :param before_start: function to be executed before the server starts listening. Takes arguments `app` instance and `loop` :param after_start: function to be executed after the server starts listening. Takes arguments `app` instance and `loop` :param before_stop: function to be executed when a stop signal is received before it is respected. Takes arguments `app` instance and `loop` :param after_stop: function to be executed when a stop signal is received after it is respected. Takes arguments `app` instance and `loop` :param debug: enables debug output (slows server) :param request_timeout: time in seconds :param response_timeout: time in seconds :param keep_alive_timeout: time in seconds :param ssl: SSLContext :param sock: Socket for the server to accept connections from :param request_max_size: size in bytes, `None` for no limit :param reuse_port: `True` for multiple workers :param loop: asyncio compatible event loop :param protocol: subclass of asyncio protocol class :param request_class: Request class to use :param access_log: disable/enable access log :param websocket_max_size: enforces the maximum size for incoming messages in bytes. :param websocket_max_queue: sets the maximum length of the queue that holds incoming messages. :param websocket_read_limit: sets the high-water limit of the buffer for incoming bytes, the low-water limit is half the high-water limit. :param websocket_write_limit: sets the high-water limit of the buffer for outgoing bytes, the low-water limit is a quarter of the high-water limit. :param is_request_stream: disable/enable Request.stream :param router: Router object :return: Nothing """ if not run_async: # create new event_loop after fork loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) connections = connections if connections is not None else set() server = partial( protocol, loop=loop, connections=connections, signal=signal, request_handler=request_handler, error_handler=error_handler, request_timeout=request_timeout, response_timeout=response_timeout, keep_alive_timeout=keep_alive_timeout, request_max_size=request_max_size, request_class=request_class, access_log=access_log, keep_alive=keep_alive, is_request_stream=is_request_stream, router=router, websocket_max_size=websocket_max_size, websocket_max_queue=websocket_max_queue, websocket_read_limit=websocket_read_limit, websocket_write_limit=websocket_write_limit, state=state, debug=debug, ) server_coroutine = loop.create_server(server, host, port, ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog) # Instead of pulling time at the end of every request, # pull it once per minute loop.call_soon(partial(update_current_time, loop)) if run_async: return server_coroutine trigger_events(before_start, loop) try: http_server = loop.run_until_complete(server_coroutine) except BaseException: logger.exception("Unable to start server") return trigger_events(after_start, loop) # Ignore SIGINT when run_multiple if run_multiple: signal_func(SIGINT, SIG_IGN) # Register signals for graceful termination if register_sys_signals: _singals = (SIGTERM, ) if run_multiple else (SIGINT, SIGTERM) for _signal in _singals: try: loop.add_signal_handler(_signal, loop.stop) except NotImplementedError: logger.warning('Sanic tried to use loop.add_signal_handler ' 'but it is not implemented on this platform.') pid = os.getpid() try: logger.info('Starting worker [%s]', pid) loop.run_forever() finally: logger.info("Stopping worker [%s]", pid) # Run the on_stop function if provided trigger_events(before_stop, loop) # Wait for event loop to finish and all connections to drain http_server.close() loop.run_until_complete(http_server.wait_closed()) # Complete all tasks on the loop signal.stopped = True for connection in connections: connection.close_if_idle() # Gracefully shutdown timeout. # We should provide graceful_shutdown_timeout, # instead of letting connection hangs forever. # Let's roughly calcucate time. start_shutdown = 0 while connections and (start_shutdown < graceful_shutdown_timeout): loop.run_until_complete(asyncio.sleep(0.1)) start_shutdown = start_shutdown + 0.1 # Force close non-idle connection after waiting for # graceful_shutdown_timeout coros = [] for conn in connections: if hasattr(conn, "websocket") and conn.websocket: coros.append(conn.websocket.close_connection()) else: conn.close() _shutdown = asyncio.gather(*coros, loop=loop) loop.run_until_complete(_shutdown) trigger_events(after_stop, loop) loop.close()
def serve( host, port, app, before_start: Optional[Iterable[ListenerType]] = None, after_start: Optional[Iterable[ListenerType]] = None, before_stop: Optional[Iterable[ListenerType]] = None, after_stop: Optional[Iterable[ListenerType]] = None, ssl: Optional[SSLContext] = None, sock: Optional[socket.socket] = None, unix: Optional[str] = None, reuse_port: bool = False, loop=None, protocol: Type[asyncio.Protocol] = HttpProtocol, backlog: int = 100, register_sys_signals: bool = True, run_multiple: bool = False, run_async: bool = False, connections=None, signal=Signal(), state=None, asyncio_server_kwargs=None, ): """Start asynchronous HTTP Server on an individual process. :param host: Address to host on :param port: Port to host on :param before_start: function to be executed before the server starts listening. Takes arguments `app` instance and `loop` :param after_start: function to be executed after the server starts listening. Takes arguments `app` instance and `loop` :param before_stop: function to be executed when a stop signal is received before it is respected. Takes arguments `app` instance and `loop` :param after_stop: function to be executed when a stop signal is received after it is respected. Takes arguments `app` instance and `loop` :param ssl: SSLContext :param sock: Socket for the server to accept connections from :param unix: Unix socket to listen on instead of TCP port :param reuse_port: `True` for multiple workers :param loop: asyncio compatible event loop :param run_async: bool: Do not create a new event loop for the server, and return an AsyncServer object rather than running it :param asyncio_server_kwargs: key-value args for asyncio/uvloop create_server method :return: Nothing """ if not run_async and not loop: # create new event_loop after fork loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if app.debug: loop.set_debug(app.debug) app.asgi = False connections = connections if connections is not None else set() protocol_kwargs = _build_protocol_kwargs(protocol, app.config) server = partial( protocol, loop=loop, connections=connections, signal=signal, app=app, state=state, unix=unix, **protocol_kwargs, ) asyncio_server_kwargs = (asyncio_server_kwargs if asyncio_server_kwargs else {}) # UNIX sockets are always bound by us (to preserve semantics between modes) if unix: sock = bind_unix_socket(unix, backlog=backlog) server_coroutine = loop.create_server( server, None if sock else host, None if sock else port, ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog, **asyncio_server_kwargs, ) if run_async: return AsyncioServer( loop=loop, serve_coro=server_coroutine, connections=connections, after_start=after_start, before_stop=before_stop, after_stop=after_stop, ) trigger_events(before_start, loop) try: http_server = loop.run_until_complete(server_coroutine) except BaseException: error_logger.exception("Unable to start server") return trigger_events(after_start, loop) # Ignore SIGINT when run_multiple if run_multiple: signal_func(SIGINT, SIG_IGN) # Register signals for graceful termination if register_sys_signals: if OS_IS_WINDOWS: ctrlc_workaround_for_windows(app) else: for _signal in [SIGTERM] if run_multiple else [SIGINT, SIGTERM]: loop.add_signal_handler(_signal, app.stop) pid = os.getpid() try: logger.info("Starting worker [%s]", pid) loop.run_forever() finally: logger.info("Stopping worker [%s]", pid) # Run the on_stop function if provided trigger_events(before_stop, loop) # Wait for event loop to finish and all connections to drain http_server.close() loop.run_until_complete(http_server.wait_closed()) # Complete all tasks on the loop signal.stopped = True for connection in connections: connection.close_if_idle() # Gracefully shutdown timeout. # We should provide graceful_shutdown_timeout, # instead of letting connection hangs forever. # Let's roughly calcucate time. graceful = app.config.GRACEFUL_SHUTDOWN_TIMEOUT start_shutdown: float = 0 while connections and (start_shutdown < graceful): loop.run_until_complete(asyncio.sleep(0.1)) start_shutdown = start_shutdown + 0.1 # Force close non-idle connection after waiting for # graceful_shutdown_timeout coros = [] for conn in connections: if hasattr(conn, "websocket") and conn.websocket: coros.append(conn.websocket.close_connection()) else: conn.close() _shutdown = asyncio.gather(*coros) loop.run_until_complete(_shutdown) trigger_events(after_stop, loop) remove_unix_socket(unix)
def serve( host, port, app, request_handler, error_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, response_timeout=60, keep_alive_timeout=5, ssl=None, sock=None, request_max_size=None, request_buffer_queue_size=100, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, register_sys_signals=True, run_multiple=False, run_async=False, connections=None, signal=Signal(), request_class=None, access_log=True, keep_alive=True, is_request_stream=False, router=None, websocket_max_size=None, websocket_max_queue=None, websocket_read_limit=2 ** 16, websocket_write_limit=2 ** 16, state=None, graceful_shutdown_timeout=15.0, asyncio_server_kwargs=None, ): """Start asynchronous HTTP Server on an individual process. :param host: Address to host on :param port: Port to host on :param request_handler: Sanic request handler with middleware :param error_handler: Sanic error handler with middleware :param before_start: function to be executed before the server starts listening. Takes arguments `app` instance and `loop` :param after_start: function to be executed after the server starts listening. Takes arguments `app` instance and `loop` :param before_stop: function to be executed when a stop signal is received before it is respected. Takes arguments `app` instance and `loop` :param after_stop: function to be executed when a stop signal is received after it is respected. Takes arguments `app` instance and `loop` :param debug: enables debug output (slows server) :param request_timeout: time in seconds :param response_timeout: time in seconds :param keep_alive_timeout: time in seconds :param ssl: SSLContext :param sock: Socket for the server to accept connections from :param request_max_size: size in bytes, `None` for no limit :param reuse_port: `True` for multiple workers :param loop: asyncio compatible event loop :param protocol: subclass of asyncio protocol class :param request_class: Request class to use :param access_log: disable/enable access log :param websocket_max_size: enforces the maximum size for incoming messages in bytes. :param websocket_max_queue: sets the maximum length of the queue that holds incoming messages. :param websocket_read_limit: sets the high-water limit of the buffer for incoming bytes, the low-water limit is half the high-water limit. :param websocket_write_limit: sets the high-water limit of the buffer for outgoing bytes, the low-water limit is a quarter of the high-water limit. :param is_request_stream: disable/enable Request.stream :param request_buffer_queue_size: streaming request buffer queue size :param router: Router object :param graceful_shutdown_timeout: How long take to Force close non-idle connection :param asyncio_server_kwargs: key-value args for asyncio/uvloop create_server method :return: Nothing """ if not run_async: # create new event_loop after fork loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) connections = connections if connections is not None else set() server = partial( protocol, loop=loop, connections=connections, signal=signal, app=app, request_handler=request_handler, error_handler=error_handler, request_timeout=request_timeout, response_timeout=response_timeout, keep_alive_timeout=keep_alive_timeout, request_max_size=request_max_size, request_class=request_class, access_log=access_log, keep_alive=keep_alive, is_request_stream=is_request_stream, router=router, websocket_max_size=websocket_max_size, websocket_max_queue=websocket_max_queue, websocket_read_limit=websocket_read_limit, websocket_write_limit=websocket_write_limit, state=state, debug=debug, ) asyncio_server_kwargs = ( asyncio_server_kwargs if asyncio_server_kwargs else {} ) server_coroutine = loop.create_server( server, host, port, ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog, **asyncio_server_kwargs ) if run_async: return server_coroutine trigger_events(before_start, loop) try: http_server = loop.run_until_complete(server_coroutine) except BaseException: logger.exception("Unable to start server") return trigger_events(after_start, loop) # Ignore SIGINT when run_multiple if run_multiple: signal_func(SIGINT, SIG_IGN) # Register signals for graceful termination if register_sys_signals: _singals = (SIGTERM,) if run_multiple else (SIGINT, SIGTERM) for _signal in _singals: try: loop.add_signal_handler(_signal, loop.stop) except NotImplementedError: logger.warning( "Sanic tried to use loop.add_signal_handler " "but it is not implemented on this platform." ) pid = os.getpid() try: logger.info("Starting worker [%s]", pid) loop.run_forever() finally: logger.info("Stopping worker [%s]", pid) # Run the on_stop function if provided trigger_events(before_stop, loop) # Wait for event loop to finish and all connections to drain http_server.close() loop.run_until_complete(http_server.wait_closed()) # Complete all tasks on the loop signal.stopped = True for connection in connections: connection.close_if_idle() # Gracefully shutdown timeout. # We should provide graceful_shutdown_timeout, # instead of letting connection hangs forever. # Let's roughly calcucate time. start_shutdown = 0 while connections and (start_shutdown < graceful_shutdown_timeout): loop.run_until_complete(asyncio.sleep(0.1)) start_shutdown = start_shutdown + 0.1 # Force close non-idle connection after waiting for # graceful_shutdown_timeout coros = [] for conn in connections: if hasattr(conn, "websocket") and conn.websocket: coros.append(conn.websocket.close_connection()) else: conn.close() _shutdown = asyncio.gather(*coros, loop=loop) loop.run_until_complete(_shutdown) trigger_events(after_stop, loop) loop.close()
def tcp_serve(host: str, port: int, serv_protocol: asyncio.Protocol, *, signal: Optional[Signal]=None, loop: asyncio.AbstractEventLoop=None, ssl: Optional[SSLContext]=None, reuse_port: bool=False, sock: Optional[socket]=None, backlog: int=100, debug: bool=False, run_multiple: bool = False, connections: set=None, run_async: bool=False, graceful_shutdown_timeout: float=15.0, current_time: bool=True) -> None: """执行一个单一进程的协程服务. Params: host (str) : - 主机地址 port (int) : - 主机端口 serv_protocol (asyncio.Protocol) : - 协议类,需要有signal字段用于保存控制是否停止的信号实例 loop (asyncio.AbstractEventLoop) : - 服务要使用的事件循环 ssl (Optional[ssl.SSLContext]) : 使用ssl默认值为None reuse_port (bool) : - 是否重用端口默认为False sock (Optional[socket]) : - 指定套接字默认为None,注意如果用socket,host和port就必须为None backlog (int) :- 传递给队列的最大连接数,默认为100, signal (Optional[Signal]) : - 与协议中公用的一个信号,用于关闭协议,需要有stopped字段 默认为None, debug (bool) : - 是否要使用debug模式 run_multiple (bool) : - 是否是多进程模式默认为False run_async (bool) : - 当设置为true的时候将创建的服务器协程返回,而不是执行这个协程,默认为False, connections (Optional[set]) : - 可以设定一个装载连接的容器,默认为None graceful_shutdown_timeout (float) : - 是否在关闭前等待一段时间,默认为15.0s current_time (bool) : - 是否使用一个全局变量来维护当前时间 """ if not run_async: # create new event_loop after fork loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) listeners = {} for event_name, reverse in HOOK_REVERSE: listeners[event_name] = LISTENERS[event_name].copy() if reverse: listeners[event_name].reverse() # 管理连接, connections = connections if connections is not None else set() # 为协议添加与服务器相关的参数 protocol_params = get_protocol_params(serv_protocol) if connections: if 'connections' in protocol_params: serv_protocol = partial(serv_protocol, connections=connections) if signal: if 'signal' in protocol_params: serv_protocol = partial(serv_protocol, signal=signal) else: warnings.warn( "protocol do not has param 'signal' ", RuntimeWarning, stacklevel=3) server_coroutine = loop.create_server( serv_protocol, host, port, ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog ) # 每分钟更新一次当前时间 if current_time: loop.call_soon(partial(update_current_time, loop)) if run_async: return server_coroutine # 执行钩子before_start trigger_events(listeners["before_server_start"], loop) try: rpc_server = loop.run_until_complete(server_coroutine) except BaseException: logger.exception("Unable to start server") return # 执行钩子after_start trigger_events(listeners["after_server_start"], loop) # 多进程时是使用SIG_IGN函数处理信号SIGINT,让这个ctrl+c的信号被屏蔽(posix平台) if run_multiple: signal_func(SIGINT, SIG_IGN) # 为优雅的关闭服务而注册信号 # 信号会被注册到loop中用于执行回调函数loop.stop # 当收到规定的信号后loop就会停止 _singals = (SIGTERM,) if run_multiple else (SIGINT, SIGTERM) for _signal in _singals: try: loop.add_signal_handler(_signal, loop.stop) except NotImplementedError as ni: print(str(ni)) logger.warning('tried to use loop.add_signal_handler ' 'but it is not implemented on this platform.') # 获取进程id方便多进程时debug pid = os.getpid() try: # 服务运行 logger.info('Starting worker [%s]', pid) loop.run_forever() finally: # 服务结束阶段 logger.info("Stopping worker [%s]", pid) # 运行钩子 before_stop trigger_events(listeners["before_server_stop"], loop) # 关闭server rpc_server.close() loop.run_until_complete(rpc_server.wait_closed()) # 关闭连接 # 完成所有空转连接的关闭工作 if signal: signal.stopped = True for connection in connections: connection.close_if_idle() # 等待由graceful_shutdown_timeout设置的时间 # 让还在运转的连接关闭,防止连接一直被挂起 start_shutdown = 0 while connections and (start_shutdown < graceful_shutdown_timeout): loop.run_until_complete(asyncio.sleep(0.1)) start_shutdown = start_shutdown + 0.1 # 在等待graceful_shutdown_timeout设置的时间后 # 强制关闭所有的连接 for conn in connections: conn.close() # 收尾阶段关闭所有协程, loop.run_until_complete(loop.shutdown_asyncgens()) # 执行钩子after_stop trigger_events(listeners["after_server_stop"], loop) # 关闭loop loop.close() logger.info("Stopped worker [%s]", pid)
def serve( request_handler, error_handler, debug=False, request_timeout=60, response_timeout=60, keep_alive_timeout=75, ssl=None, sock=None, request_max_size=100000000, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, register_sys_signals=True, run_multiple=False, run_async=False, connections=None, signal=Signal(), request_class=None, access_log=True, keep_alive=True, is_request_stream=False, router=None, websocket_max_size=None, websocket_max_queue=None, websocket_read_limit=2 ** 16, websocket_write_limit=2 ** 16, state=None, ): """Start asynchronous HTTP Server on an individual process. :param request_handler: Sanic request handler with middleware :param error_handler: Sanic error handler with middleware :param debug: enables debug output (slows server) :param request_timeout: time in seconds :param response_timeout: time in seconds :param keep_alive_timeout: time in seconds :param ssl: SSLContext :param sock: Socket for the server to accept connections from :param request_max_size: size in bytes, `None` for no limit :param reuse_port: `True` for multiple workers :param loop: asyncio compatible event loop :param protocol: subclass of asyncio protocol class :param request_class: Request class to use :param access_log: disable/enable access log :param websocket_max_size: enforces the maximum size for incoming messages in bytes. :param websocket_max_queue: sets the maximum length of the queue that holds incoming messages. :param websocket_read_limit: sets the high-water limit of the buffer for incoming bytes, the low-water limit is half the high-water limit. :param websocket_write_limit: sets the high-water limit of the buffer for outgoing bytes, the low-water limit is a quarter of the high-water limit. :param is_request_stream: disable/enable Request.stream :return: Nothing """ if not run_async: # create new event_loop after fork loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) connections = connections if connections is not None else set() server = partial( protocol, loop=loop, connections=connections, signal=signal, request_handler=request_handler, error_handler=error_handler, request_timeout=request_timeout, response_timeout=response_timeout, keep_alive_timeout=keep_alive_timeout, request_max_size=request_max_size, request_class=request_class, access_log=access_log, keep_alive=keep_alive, is_request_stream=is_request_stream, router=router, websocket_max_size=websocket_max_size, websocket_max_queue=websocket_max_queue, websocket_read_limit=websocket_read_limit, websocket_write_limit=websocket_write_limit, state=state, debug=debug, ) create_server_kwargs = dict( ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog, ) if constants.is_py37(): create_server_kwargs.update( start_serving=False ) server_coroutine = loop.create_server( server, **create_server_kwargs ) # Instead of pulling time at the end of every request, # pull it once per minute loop.call_soon(partial(update_current_time, loop)) if run_async: return server_coroutine try: http_server = loop.run_until_complete(server_coroutine) except BaseException: logger.exception("Unable to start server") return # Ignore SIGINT when run_multiple if run_multiple: signal_func(SIGINT, SIG_IGN) # Register signals for graceful termination if register_sys_signals: _signals = (SIGTERM,) if run_multiple else (SIGINT, SIGTERM) for _signal in _signals: try: loop.add_signal_handler(_signal, loop.stop) except NotImplementedError: logger.warning( "Sanic tried to use loop.add_signal_handler " "but it is not implemented on this platform." ) def start_serving(): if constants.is_py37(): loop.run_until_complete(http_server.start_serving()) def start(): pid = os.getpid() try: logger.info("Starting worker [%s]", pid) if constants.is_py37(): loop.run_until_complete(http_server.serve_forever()) else: loop.run_forever() finally: http_server.close() loop.run_until_complete(http_server.wait_closed()) loop.run_until_complete(loop.shutdown_asyncgens()) loop.close() return start_serving, start
def multiple_tcp_serve(server_settings: Dict[str, Any], workers: int) -> None: """启动一个多进程的tcp服务,他们共享同一个socket对象. 用multiple模式在每个子进程执行tcp服务,当执行完成后统一的回收资源 Params: server_settings (Dicct[str, Any]) : - 每个单一进程的设置, workers (int) : - 执行的进程数 """ server_settings['reuse_port'] = True server_settings['run_multiple'] = True # Handling when custom socket is not provided. if server_settings.get('sock') is None: sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind((server_settings['host'], server_settings['port'])) sock.set_inheritable(True) server_settings['sock'] = sock server_settings['host'] = None server_settings['port'] = None def sig_handler(signal: Any, frame: Any): """向子进程传送SIGTERM信号,用于关闭所有子进程中运行的事件循环. Params: signal (Any) : - 要处理的信号 frame (Any) : - 执行到的帧 """ status = [] for process in processes: statu = process.is_alive() status.append(statu) if statu: os.kill(process.pid, SIGTERM) if any(status): logger.info( """Received signal {}. Shutting down. You may need to enter Ctrl+C again. """.format(Signals(signal).name)) else: raise MultipleProcessDone("all process not alive") signal_func(SIGINT, sig_handler) signal_func(SIGTERM, sig_handler) processes = [] for _ in range(workers): process = Process(target=tcp_serve, kwargs=server_settings) process.daemon = True process.start() processes.append(process) try: while True: pass except MultipleProcessDone as done: logger.info(str(done)) except Exception as e: raise e finally: for process in processes: process.join() # 使用join同步后,只有进程运行结束了才关闭子进程 for process in processes: process.terminate() server_settings.get('sock').close() logger.info("Shutting down done.")