Browse Source

Rework server_base to spawn a single task

patch-2
Neil Booth 7 years ago
parent
commit
55ef1ab157
  1. 65
      electrumx/lib/server_base.py

65
electrumx/lib/server_base.py

@ -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…
Cancel
Save