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()
Beispiel #2
0
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)
Beispiel #3
0
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()
Beispiel #5
0
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()
Beispiel #6
0
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()
Beispiel #7
0
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()
Beispiel #8
0
    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()
Beispiel #9
0
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()
Beispiel #10
0
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)
Beispiel #11
0
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()
Beispiel #12
0
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)
Beispiel #13
0
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.")