Added new process-pool runner based on AMPoule (integrated into Evennia).

This allows e.g. utils.utils.run_async to offload long-running functions
to a completely different subprocess entirely, offering real parallelism.

Implementation is still experimental, notably not all objects can be
transferred safely across the wire; also there is no concept of
updating caches yet - so adding an object from the subprocess side
will not be known in the main thread yet (since caches cannot yet tell
the underlying database has changed).
This commit is contained in:
Griatch 2012-09-02 10:10:22 +02:00
parent dcc7f29a91
commit f5a889e40c
22 changed files with 2322 additions and 60 deletions

View file

@ -39,9 +39,6 @@ SERVER_RESTART = os.path.join(settings.GAME_DIR, 'server.restart')
# module containing hook methods
SERVER_HOOK_MODULE = mod_import(settings.AT_SERVER_STARTSTOP_MODULE)
# i18n
from django.utils.translation import ugettext as _
#------------------------------------------------------------
# Evennia Server settings
#------------------------------------------------------------
@ -53,11 +50,25 @@ AMP_ENABLED = True
AMP_HOST = settings.AMP_HOST
AMP_PORT = settings.AMP_PORT
PROCPOOL_ENABLED = settings.PROCPOOL_ENABLED
PROCPOOL_DEBUG = settings.PROCPOOL_DEBUG
PROCPOOL_MIN_NPROC = settings.PROCPOOL_MIN_NPROC
PROCPOOL_MAX_NPROC = settings.PROCPOOL_MAX_NPROC
PROCPOOL_TIMEOUT = settings.PROCPOOL_TIMEOUT
PROCPOOL_IDLETIME = settings.PROCPOOL_IDLETIME
PROCPOOL_HOST = settings.PROCPOOL_HOST
PROCPOOL_PORT = settings.PROCPOOL_PORT
PROCPOOL_INTERFACE = settings.PROCPOOL_INTERFACE
PROCPOOL_UID = settings.PROCPOOL_UID
PROCPOOL_GID = settings.PROCPOOL_GID
PROCPOOL_DIRECTORY = settings.PROCPOOL_DIRECTORY
# server-channel mappings
IMC2_ENABLED = settings.IMC2_ENABLED
IRC_ENABLED = settings.IRC_ENABLED
RSS_ENABLED = settings.RSS_ENABLED
#------------------------------------------------------------
# Evennia Main Server object
#------------------------------------------------------------
@ -208,7 +219,10 @@ class Evennia(object):
Outputs server startup info to the terminal.
"""
print ' %(servername)s Server (%(version)s) started.' % {'servername': SERVERNAME, 'version': VERSION}
print ' amp (Portal): %s' % AMP_PORT
print ' amp (to Portal): %s' % AMP_PORT
if PROCPOOL_ENABLED:
print ' amp (Process Pool): %s' % PROCPOOL_PORT
def set_restart_mode(self, mode=None):
"""
@ -319,6 +333,49 @@ if AMP_ENABLED:
amp_service.setName("EvenniaPortal")
EVENNIA.services.addService(amp_service)
# The ampoule twisted extension manages asynchronous process pools
# via an AMP port. It can be used to offload expensive operations
# to another process asynchronously.
if PROCPOOL_ENABLED:
from src.utils.ampoule import main as ampoule_main
from src.utils.ampoule import service as ampoule_service
from src.utils.ampoule import pool as ampoule_pool
from src.utils.ampoule.main import BOOTSTRAP as _BOOTSTRAP
from src.server.procpool import ProcPoolChild
# for some reason absolute paths don't work here, only relative ones.
apackages = ("twisted",
os.path.join(os.pardir, "src", "utils", "ampoule"),
os.path.join(os.pardir, "ev"),
os.path.join(os.pardir))
aenv = {"DJANGO_SETTINGS_MODULE":"settings",
"DATABASE_NAME":settings.DATABASES.get("default", {}).get("NAME") or settings.DATABASE_NAME}
if PROCPOOL_DEBUG:
_BOOTSTRAP = _BOOTSTRAP % "log.startLogging(sys.stderr)"
else:
_BOOTSTRAP = _BOOTSTRAP % ""
procpool_starter = ampoule_main.ProcessStarter(packages=apackages,
env=aenv,
path=PROCPOOL_DIRECTORY,
uid=PROCPOOL_UID,
gid=PROCPOOL_GID,
bootstrap=_BOOTSTRAP,
childReactor=os.name == 'nt' and "select" or "epoll")
procpool = ampoule_pool.ProcessPool(name="ProcPool",
min=PROCPOOL_MIN_NPROC,
max=PROCPOOL_MAX_NPROC,
recycleAfter=500,
ampChild=ProcPoolChild,
starter=procpool_starter)
procpool_service = ampoule_service.AMPouleService(procpool,
ProcPoolChild,
PROCPOOL_PORT,
PROCPOOL_INTERFACE)
procpool_service.setName("ProcPool")
EVENNIA.services.addService(procpool_service)
if IRC_ENABLED: