mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Added django/core/servers/fastcgi.py and manage.py 'runfcgi' option. Thanks, jcrasta@gmail.com
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3174 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
a4b11826a7
commit
32228d2031
1
AUTHORS
1
AUTHORS
@ -65,6 +65,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Kieran Holland <http://www.kieranholland.com>
|
||||
Robert Rock Howard <http://djangomojo.com/>
|
||||
Jason Huggins <http://www.jrandolph.com/blog/>
|
||||
jcrasta@gmail.com
|
||||
Michael Josephson <http://www.sdjournal.com/>
|
||||
jpellerin@gmail.com
|
||||
junzhang.jn@gmail.com
|
||||
|
@ -1081,6 +1081,12 @@ def dbshell():
|
||||
runshell()
|
||||
dbshell.args = ""
|
||||
|
||||
def runfcgi(args):
|
||||
"""Run this project as a FastCGI application. requires flup."""
|
||||
from django.core.servers.fastcgi import runfastcgi
|
||||
runfastcgi(args)
|
||||
runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
|
||||
|
||||
# Utilities for command-line script
|
||||
|
||||
DEFAULT_ACTION_MAPPING = {
|
||||
@ -1091,6 +1097,7 @@ DEFAULT_ACTION_MAPPING = {
|
||||
'inspectdb': inspectdb,
|
||||
'install': install,
|
||||
'reset': reset,
|
||||
'runfcgi': runfcgi,
|
||||
'runserver': runserver,
|
||||
'shell': run_shell,
|
||||
'sql': get_sql_create,
|
||||
@ -1210,6 +1217,8 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
|
||||
except ValueError:
|
||||
addr, port = '', args[1]
|
||||
action_mapping[action](addr, port)
|
||||
elif action == 'runfcgi':
|
||||
action_mapping[action](args[1:])
|
||||
else:
|
||||
from django.db import models
|
||||
try:
|
||||
|
147
django/core/servers/fastcgi.py
Normal file
147
django/core/servers/fastcgi.py
Normal file
@ -0,0 +1,147 @@
|
||||
"""
|
||||
FastCGI server that implements the WSGI protocol.
|
||||
|
||||
Uses the flup python package: http://www.saddi.com/software/flup/
|
||||
|
||||
This is a adaptation of the flup package to add FastCGI server support
|
||||
to run Django apps from Web servers that support the FastCGI protocol.
|
||||
This module can be run standalone or from the django-admin / manage.py
|
||||
scripts using the "runfcgi" directive.
|
||||
|
||||
Run with the extra option "help" for a list of additional options you can
|
||||
pass to this server.
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
|
||||
__version__ = "0.1"
|
||||
__all__ = ["runfastcgi"]
|
||||
|
||||
FASTCGI_HELP = r"""runfcgi:
|
||||
Run this project as a fastcgi application. To do this, the
|
||||
flup package from http://www.saddi.com/software/flup/ is
|
||||
required.
|
||||
|
||||
Usage:
|
||||
django-admin.py runfcgi --settings=yourproject.settings [fcgi settings]
|
||||
manage.py runfcgi [fcgi settings]
|
||||
|
||||
Optional Fcgi settings: (setting=value)
|
||||
host=HOSTNAME hostname to listen on..
|
||||
port=PORTNUM port to listen on.
|
||||
socket=FILE UNIX socket to listen on.
|
||||
method=IMPL prefork or threaded (default prefork)
|
||||
maxspare=NUMBER max number of spare processes to keep running.
|
||||
minspare=NUMBER min number of spare processes to prefork.
|
||||
maxchildren=NUMBER hard limit number of processes in prefork mode.
|
||||
daemonize=BOOL whether to detach from terminal.
|
||||
pidfile=FILE write the spawned process-id to this file.
|
||||
workdir=DIRECTORY change to this directory when daemonizing
|
||||
|
||||
Examples:
|
||||
Run a "standard" fastcgi process on a file-descriptor
|
||||
(for webservers which spawn your processes for you)
|
||||
$ manage.py runfcgi method=threaded
|
||||
|
||||
Run a fastcgi server on a TCP host/port
|
||||
$ manage.py runfcgi method=prefork host=127.0.0.1 port=8025
|
||||
|
||||
Run a fastcgi server on a UNIX domain socket (posix platforms only)
|
||||
$ manage.py runfcgi method=prefork socket=/tmp/fcgi.sock
|
||||
|
||||
Run a fastCGI as a daemon and write the spawned PID in a file
|
||||
$ manage.py runfcgi socket=/tmp/fcgi.sock method=prefork \
|
||||
daemonize=true pidfile=/var/run/django-fcgi.pid
|
||||
|
||||
"""
|
||||
|
||||
FASTCGI_OPTIONS = {
|
||||
'host': None,
|
||||
'port': None,
|
||||
'socket': None,
|
||||
'method': 'fork',
|
||||
'daemonize': None,
|
||||
'workdir': '/',
|
||||
'pidfile': None,
|
||||
'maxspare': 5,
|
||||
'minspare': 2,
|
||||
'maxchildren': 50,
|
||||
}
|
||||
|
||||
def fastcgi_help(message=None):
|
||||
print FASTCGI_HELP
|
||||
if message:
|
||||
print message
|
||||
return False
|
||||
|
||||
def runfastcgi(argset):
|
||||
options = FASTCGI_OPTIONS.copy()
|
||||
for x in argset:
|
||||
if "=" in x:
|
||||
k, v = x.split('=', 1)
|
||||
else:
|
||||
k, v = x, True
|
||||
options[k.lower()] = v
|
||||
|
||||
if "help" in options:
|
||||
return fastcgi_help()
|
||||
|
||||
try:
|
||||
import flup
|
||||
except ImportError, e:
|
||||
print >> sys.stderr, "ERROR: %s" % e
|
||||
print >> sys.stderr, " Unable to load the flup package. In order to run django"
|
||||
print >> sys.stderr, " as a FastCGI application, you will need to get flup from"
|
||||
print >> sys.stderr, " http://www.saddi.com/software/flup/ If you've already"
|
||||
print >> sys.stderr, " installed flup, then make sure you have it in your PYTHONPATH."
|
||||
return False
|
||||
|
||||
if options['method'] in ('prefork', 'fork'):
|
||||
from flup.server.fcgi_fork import WSGIServer
|
||||
wsgi_opts = {
|
||||
'maxSpare': int(options["maxspare"]),
|
||||
'minSpare': int(options["minspare"]),
|
||||
'maxChildren': int(options["maxchildren"]),
|
||||
}
|
||||
elif options['method'] in ('thread', 'threaded'):
|
||||
from flup.server.fcgi import WSGIServer
|
||||
wsgi_opts = {}
|
||||
else:
|
||||
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
|
||||
|
||||
# Prep up and go
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
|
||||
if options["host"] and options["port"] and not options["socket"]:
|
||||
wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
|
||||
elif options["socket"] and not options["host"] and not options["port"]:
|
||||
wsgi_opts['bindAddress'] = options["socket"]
|
||||
elif not options["socket"] and not options["host"] and not options["port"]:
|
||||
wsgi_opts['bindAddress'] = None
|
||||
else:
|
||||
return fastcgi_help("Invalid combination of host, port, socket.")
|
||||
|
||||
if options["daemonize"] is None:
|
||||
# Default to daemonizing if we're running on a socket/named pipe.
|
||||
daemonize = (wsgi_opts['bindAddress'] is not None)
|
||||
else:
|
||||
if options["daemonize"].lower() in ('true', 'yes', 't'):
|
||||
daemonize = True
|
||||
elif options["daemonize"].lower() in ('false', 'no', 'f'):
|
||||
daemonize = False
|
||||
else:
|
||||
return fastcgi_help("ERROR: Invalid option for daemonize parameter.")
|
||||
|
||||
if daemonize:
|
||||
from django.utils.daemonize import become_daemon
|
||||
become_daemon(our_home_dir=options["workdir"])
|
||||
|
||||
if options["pidfile"]:
|
||||
fp = open(options["pidfile"], "w")
|
||||
fp.write("%d\n" % os.getpid())
|
||||
fp.close()
|
||||
|
||||
WSGIServer(WSGIHandler(), **wsgi_opts).run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
runfastcgi(sys.argv[1:])
|
55
django/utils/daemonize.py
Normal file
55
django/utils/daemonize.py
Normal file
@ -0,0 +1,55 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
if os.name == 'posix':
|
||||
def become_daemon(our_home_dir='.', out_log='/dev/null', err_log='/dev/null'):
|
||||
"Robustly turn into a UNIX daemon, running in our_home_dir."
|
||||
# First fork
|
||||
try:
|
||||
if os.fork() > 0:
|
||||
sys.exit(0) # kill off parent
|
||||
except OSError, e:
|
||||
sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
os.setsid()
|
||||
os.chdir(our_home_dir)
|
||||
os.umask(0)
|
||||
|
||||
# Second fork
|
||||
try:
|
||||
if os.fork() > 0:
|
||||
sys.exit(0)
|
||||
except OSError, e:
|
||||
sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
|
||||
sys.exit(1)
|
||||
|
||||
si = open('/dev/null', 'r')
|
||||
so = open(out_log, 'a+', 0)
|
||||
se = open(err_log, 'a+', 0)
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
else:
|
||||
def become_daemon(our_home_dir='.', out_log=None, err_log=None):
|
||||
"""
|
||||
If we're not running under a POSIX system, just simulate the daemon
|
||||
mode by doing redirections and directory changing.
|
||||
"""
|
||||
os.chdir(our_home_dir)
|
||||
os.umask(0)
|
||||
sys.stdin.close()
|
||||
sys.stdout.close()
|
||||
sys.stderr.close()
|
||||
if err_log:
|
||||
sys.stderr = open(err_log, 'a', 0)
|
||||
else:
|
||||
sys.stderr = NullDevice()
|
||||
if out_log:
|
||||
sys.stdout = open(out_log, 'a', 0)
|
||||
else:
|
||||
sys.stdout = NullDevice()
|
||||
|
||||
class NullDevice:
|
||||
"A writeable object that writes to nowhere -- like /dev/null."
|
||||
def write(self, s):
|
||||
pass
|
Loading…
Reference in New Issue
Block a user