Re: Organizing a Quixote/Durus multiprocess application

"Mike Orr" <[email protected]>
Newsgroups gmane.comp.web.quixote.user,gmane.comp.python.durus
Message-ID <[email protected]>
Here's what I've got so far.  Does it look reasonable?

I first divert sys.stdout and sys.stderr to a logfile unless envvar
"NO_LOG" is set.  This will be used by all subprocesses.  I don't want
some output sent to the console or swallowed up depending on where the
exception hits.  Then I fork and (in the child) start the Durus server
for the sessions db, and (in the parent) start the SCGI server.  I
catch KeyboardInterrupt in both cases to avoid a traceback in the log
file.  I'm using None for both the Quixote error log and the Durus
error log to prevent those packages from redirecting it further.

It took a while to get the file ownerships and umask correct, but I
finally got it working as user 'apache' from the command line.
But if I start it as a daemon and then stop it, it doesn't kill the
Durus server.  That must be because it's using SIGTERM instead of
SIGINT.  I added some code to kill it manually if it's still running,
but that doesn't help.

I may switch to a TCP socket if it gets too frustrating managing the
Unix socket, but I don't think that will help the current problem

Another issue is getting rid of Durus's "sys.stdout already customized.
sys.stderr already customized." messages in the log file.  I set
logginglevel to 101 but that doesn't help because the messages are
sent before that gets set.  I may replace durus.logging.direct_output
to get rid of them.

Another issue is in scgi_server.  I sometimes get a KeyError on
"self.reap_children(self.children[pid])".  I assume that means the
SCGI subprocess has already died, because one of them gave a
permission exception earlier.  I think scgi_server should silently
ignore this error?

I am happy that Quixote and Durus and scgi_server are simple enough
you can follow the code and usually fix problems yourself.  That has
helped me quite a bit in my last few applications.  I'm just getting
into new territory with Unix sockets and fork and how child processes
terminate.

-- 
Mike Orr <[email protected]>

_______________________________________________
Quixote-users mailing list
[email protected]
http://mail.mems-exchange.org/mailman/listinfo/quixote-users
cameo_server.py (text/x-python, 2.6 KB)
#!/usr/bin/env python
import codecs, os, sys
import init_app   # Must import before any cameo or hazweb modules.
import config

os.umask(02)  # Create all files/sockets mode "rw-rw-r--".
if not os.environ.get("NO_LOG"):
    _logstream = open(config.error_log, "ab", 1)
    sys.stdout = _logstream
    sys.stderr = _logstream
    del _logstream

import errno,  os, signal, sys, time
from durus.client_storage import ClientStorage
from durus.connection import Connection
from durus.run_durus import start_durus, stop_durus
from durus.storage_server import SocketAddress
import quixote
from quixote.publish import Publisher
from quixote.server import scgi_server
from session2.SessionManager import SessionManager
from session2.store.DurusSessionStore import DurusSessionStore
import init_app
import config
from cameo.controllers.root import RootDirectory
from cameo.logger import CameoLogger
from cameo.model.cameo_api import Cameo
from cameo.session import Session

# SCGI parameters
HOST = "127.0.0.1"
PORT = 3004
MAX_CHILDREN = 3

quixote.DEFAULT_CHARSET = config.output_encoding
def session_manager():
    """For the multiuser web server."""
    storage = ClientStorage(address=config.sessions_socket)
    conn = Connection(storage, cache_size=100)
    store = DurusSessionStore(conn)
    return SessionManager(store, Session)

def create_publisher():
    logger = CameoLogger(
        access_log=config.access_log,
        error_log=None,  # Already handled at top of this module.
        error_email=None)
    publisher = Publisher(RootDirectory(),
        display_exceptions=config.display_exceptions,
        session_cookie_name="CAMEO_Session", 
        session_manager=session_manager(),
        logger=logger)
    config.cameo = Cameo(config.db_file, readonly=True)
    config.cameo.precache()
    return publisher

def log_friendly(func, *args, **kw):
    try:
        func(*args, **kw)
    except KeyboardInterrupt:
        pass  # Don't put unnecessary traceback in the log file.

def main():
    pid = os.fork()  # Raises OSError.
    if pid == 0:  # Child process.
        address = SocketAddress.new(address=config.sessions_socket)
        #print "In Durus process", os.getpid()
        log_friendly(start_durus, logfile=None, logginglevel=99, 
            file=config.sessions_file,
            repair=False, readonly=False, address=address)
        return
    # Parent process
    log_friendly(scgi_server.run, create_publisher, host=HOST, port=PORT,
        max_children=MAX_CHILDREN)
    try:
        os.kill(pid, signal.SIGTERM)
    except OSError, e:
        if e.errno != errno.NOSCH:  # No such process.        
            raise

if __name__ == "__main__":  main()
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.