Added multiprocessing

This commit is contained in:
Channel Cat
2016-10-18 01:22:49 -07:00
parent 18aa937f29
commit 6f105a647e
10 changed files with 262 additions and 19 deletions

36
sanic/__main__.py Normal file
View File

@@ -0,0 +1,36 @@
from argparse import ArgumentParser
from importlib import import_module
from .log import log
from .sanic import Sanic
if __name__ == "__main__":
parser = ArgumentParser(prog='sanic')
parser.add_argument('--host', dest='host', type=str, default='127.0.0.1')
parser.add_argument('--port', dest='port', type=int, default=8000)
parser.add_argument('--workers', dest='workers', type=int, default=1, )
parser.add_argument('--debug', dest='debug', action="store_true")
parser.add_argument('module')
args = parser.parse_args()
try:
module_parts = args.module.split(".")
module_name = ".".join(module_parts[:-1])
app_name = module_parts[-1]
module = import_module(module_name)
app = getattr(module, app_name, None)
if type(app) is not Sanic:
raise ValueError("Module is not a Sanic app, it is a {}. "
"Perhaps you meant {}.app?"
.format(type(app).__name__, args.module))
app.run(host=args.host, port=args.port,
workers=args.workers, debug=args.debug)
except ImportError:
log.error("No module named {} found.\n"
" Example File: project/sanic_server.py -> app\n"
" Example Module: project.sanic_server.app"
.format(module_name))
except ValueError as e:
log.error("{}".format(e))

View File

@@ -1,5 +1,9 @@
import asyncio
from argparse import ArgumentParser
from asyncio import get_event_loop
from inspect import isawaitable
from multiprocessing import Process, Event
from signal import signal, SIGTERM, SIGINT
from time import sleep
from traceback import format_exc
from .config import Config
@@ -167,7 +171,7 @@ class Sanic:
# -------------------------------------------------------------------- #
def run(self, host="127.0.0.1", port=8000, debug=False, after_start=None,
before_stop=None):
before_stop=None, sock=None, workers=1):
"""
Runs the HTTP Server and listens until keyboard interrupt or term
signal. On termination, drains connections before closing.
@@ -178,11 +182,24 @@ class Sanic:
listening
:param before_stop: Function to be executed when a stop signal is
received before it is respected
:param sock: Socket for the server to accept connections from
:param workers: Number of processes
received before it is respected
:return: Nothing
"""
self.error_handler.debug = True
self.debug = debug
server_settings = {
'host': host,
'port': port,
'sock': sock,
'debug': debug,
'request_handler': self.handle_request,
'request_timeout': self.config.REQUEST_TIMEOUT,
'request_max_size': self.config.REQUEST_MAX_SIZE,
}
if debug:
log.setLevel(logging.DEBUG)
log.debug(self.config.LOGO)
@@ -191,23 +208,61 @@ class Sanic:
log.info('Goin\' Fast @ http://{}:{}'.format(host, port))
try:
serve(
host=host,
port=port,
debug=debug,
after_start=after_start,
before_stop=before_stop,
request_handler=self.handle_request,
request_timeout=self.config.REQUEST_TIMEOUT,
request_max_size=self.config.REQUEST_MAX_SIZE,
)
if workers == 1:
server_settings['after_start'] = after_start
server_settings['before_stop'] = before_stop
serve(**server_settings)
else:
log.info('Spinning up {} workers...'.format(workers))
self.serve_multiple(server_settings, workers)
except Exception as e:
log.exception(
'Experienced exception while trying to serve: {}'.format(e))
pass
log.info("Server Stopped")
def stop(self):
"""
This kills the Sanic
"""
asyncio.get_event_loop().stop()
get_event_loop().stop()
@staticmethod
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:
"""
server_settings['reuse_port'] = True
# Create a stop event to be triggered by a signal
if not stop_event:
stop_event = Event()
signal(SIGINT, lambda s, f: stop_event.set())
signal(SIGTERM, lambda s, f: stop_event.set())
processes = []
for w in range(workers):
process = Process(target=serve, kwargs=server_settings)
process.start()
processes.append(process)
# Infinitely wait for the stop event
try:
while not stop_event.is_set():
sleep(0.3)
except:
pass
log.info('Spinning down workers...')
for process in processes:
process.terminate()
for process in processes:
process.join()

View File

@@ -158,8 +158,8 @@ class HttpProtocol(asyncio.Protocol):
def serve(host, port, request_handler, after_start=None, before_stop=None,
debug=False, request_timeout=60,
request_max_size=None):
debug=False, request_timeout=60, sock=None,
request_max_size=None, reuse_port=False):
# Create Event Loop
loop = async_loop.new_event_loop()
asyncio.set_event_loop(loop)
@@ -176,7 +176,7 @@ def serve(host, port, request_handler, after_start=None, before_stop=None,
request_handler=request_handler,
request_timeout=request_timeout,
request_max_size=request_max_size,
), host, port)
), host, port, reuse_port=reuse_port, sock=sock)
try:
http_server = loop.run_until_complete(server_coroutine)
except Exception as e:
@@ -217,4 +217,3 @@ def serve(host, port, request_handler, after_start=None, before_stop=None,
loop.run_until_complete(asyncio.sleep(0.1))
loop.close()
log.info("Server Stopped")