Rework server_base to spawn a single task
This commit is contained in:
parent
5ee5a54f5a
commit
55ef1ab157
@ -12,7 +12,6 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from electrumx.lib.tasks import Tasks
|
|
||||||
from electrumx.lib.util import class_logger
|
from electrumx.lib.util import class_logger
|
||||||
|
|
||||||
|
|
||||||
@ -22,9 +21,8 @@ class ServerBase(object):
|
|||||||
Derived classes are expected to:
|
Derived classes are expected to:
|
||||||
|
|
||||||
- set PYTHON_MIN_VERSION and SUPPRESS_MESSAGES as appropriate
|
- set PYTHON_MIN_VERSION and SUPPRESS_MESSAGES as appropriate
|
||||||
- implement the start_servers() coroutine, called from the run() method.
|
- implement the serve() coroutine, called from the run() method.
|
||||||
Upon return the event loop runs until the shutdown signal is received.
|
Upon return the event loop runs until the shutdown signal is received.
|
||||||
- implement the shutdown() coroutine
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
SUPPRESS_MESSAGES = [
|
SUPPRESS_MESSAGES = [
|
||||||
@ -45,7 +43,6 @@ class ServerBase(object):
|
|||||||
self.logger = class_logger(__name__, self.__class__.__name__)
|
self.logger = class_logger(__name__, self.__class__.__name__)
|
||||||
self.logger.info(f'Python version: {sys.version}')
|
self.logger.info(f'Python version: {sys.version}')
|
||||||
self.env = env
|
self.env = env
|
||||||
self.tasks = Tasks()
|
|
||||||
|
|
||||||
# Sanity checks
|
# Sanity checks
|
||||||
if sys.version_info < self.PYTHON_MIN_VERSION:
|
if sys.version_info < self.PYTHON_MIN_VERSION:
|
||||||
@ -59,23 +56,13 @@ class ServerBase(object):
|
|||||||
'To continue as root anyway, restart with '
|
'To continue as root anyway, restart with '
|
||||||
'environment variable ALLOW_ROOT non-empty')
|
'environment variable ALLOW_ROOT non-empty')
|
||||||
|
|
||||||
# Trigger this event to cleanly shutdown
|
async def serve(self, shutdown_event):
|
||||||
self.shutdown_event = asyncio.Event()
|
'''Override to provide the main server functionality.
|
||||||
|
Run as a task that will be cancelled to request shutdown.
|
||||||
|
|
||||||
async def start_servers(self):
|
Setting the event also shuts down the server.
|
||||||
'''Override to perform initialization that requires the event loop,
|
'''
|
||||||
and start servers.'''
|
shutdown_event.set()
|
||||||
pass
|
|
||||||
|
|
||||||
async def shutdown(self):
|
|
||||||
'''Override to perform the shutdown sequence, if any.'''
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_signal(self, signame):
|
|
||||||
'''Call on receipt of a signal to cleanly shutdown.'''
|
|
||||||
self.logger.warning('received {} signal, initiating shutdown'
|
|
||||||
.format(signame))
|
|
||||||
self.shutdown_event.set()
|
|
||||||
|
|
||||||
def on_exception(self, loop, context):
|
def on_exception(self, loop, context):
|
||||||
'''Suppress spurious messages it appears we cannot control.'''
|
'''Suppress spurious messages it appears we cannot control.'''
|
||||||
@ -90,30 +77,34 @@ class ServerBase(object):
|
|||||||
'''Run the server application:
|
'''Run the server application:
|
||||||
|
|
||||||
- record start time
|
- record start time
|
||||||
- set the event loop policy as specified by the environment
|
- install SIGINT and SIGTERM handlers to trigger shutdown_event
|
||||||
- install SIGINT and SIGKILL handlers to trigger shutdown_event
|
|
||||||
- set loop's exception handler to suppress unwanted messages
|
- set loop's exception handler to suppress unwanted messages
|
||||||
- run the event loop until start_servers() completes
|
- run the event loop until serve() completes
|
||||||
- run the event loop until shutdown is signalled
|
|
||||||
'''
|
'''
|
||||||
self.start_time = time.time()
|
def on_signal(signame):
|
||||||
|
shutdown_event.set()
|
||||||
|
self.logger.warning(f'received {signame} signal, '
|
||||||
|
f'initiating shutdown')
|
||||||
|
|
||||||
|
self.start_time = time.time()
|
||||||
for signame in ('SIGINT', 'SIGTERM'):
|
for signame in ('SIGINT', 'SIGTERM'):
|
||||||
loop.add_signal_handler(getattr(signal, signame),
|
loop.add_signal_handler(getattr(signal, signame),
|
||||||
partial(self.on_signal, signame))
|
partial(on_signal, signame))
|
||||||
loop.set_exception_handler(self.on_exception)
|
loop.set_exception_handler(self.on_exception)
|
||||||
|
|
||||||
self.tasks.create_task(self.start_servers())
|
shutdown_event = asyncio.Event()
|
||||||
|
task = loop.create_task(self.serve(shutdown_event))
|
||||||
# Wait for shutdown to be signalled, and log it.
|
try:
|
||||||
# Derived classes may want to provide a shutdown() coroutine.
|
# Wait for shutdown to be signalled, and log it.
|
||||||
await self.shutdown_event.wait()
|
await shutdown_event.wait()
|
||||||
self.logger.info('shutting down')
|
self.logger.info('shutting down')
|
||||||
await self.shutdown()
|
task.cancel()
|
||||||
|
await task
|
||||||
# Let the loop clean itself up; prevents some silly logs
|
finally:
|
||||||
await asyncio.sleep(0.001)
|
await loop.shutdown_asyncgens()
|
||||||
|
|
||||||
|
# Prevent some silly logs
|
||||||
|
await asyncio.sleep(0)
|
||||||
# Finally, work around an apparent asyncio bug that causes log
|
# Finally, work around an apparent asyncio bug that causes log
|
||||||
# spew on shutdown for partially opened SSL sockets
|
# spew on shutdown for partially opened SSL sockets
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user