Django Gotchas Part 3 - Windows Services

Continuing with my series of Django Gotchas in this post I will speak about something I haven’t seen anywhere else: Django as a Windows Service (yuck!).

I recently had the inconvenience pleasure of deploying a Django project under Apache on Windows. Even though I haven’t tested it myself I have heard a lot about how mod_python is a memory hog and since the server on which I was deploying offered me a whopping 256M of RAM I didn’t even consider it. I decided to deploy under mod_fastcgi.

Here’s where the fun began.

I won’t go into the details but python, flup and eunuchs just didn’t seem to get along in Windows … I was at a loss until I came across this Django bug report in which the user snaury confirms that it does work! It works, but not as you’d expect it to work.

Configuring Apache to speak fastcgi to Django wasn’t anything out of this world and I had the whole setup going in a bit, things got really interesting when the following dialogue took place

Manager: Nice, so how will Django run? Will it be a Windows service?
Me: Hrm … sure …

Fortunately snaury had mentioned that Django was being run as a service so at least I knew it could be done.

It turns out to be that writing Windows services in Python isn’t difficult at all! After reading Essien’s explanation on how to do so I was ready to go.

Oh’rlly?

Or so I thought.

Writing Windows services in Python basically involved importing the appropriate modules, writing a class that inherits from win32serviceutil.serviceFramework and overriding the methods SvcDoRun and SvcDoStop. Easy eh?

Actually my entire Django service is about 41 35 lines of code (counting blank lines) and you can see it all here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import win32service
import win32serviceutil
import sys
sys.path.append("c:\\django_projects\\django-trunk")
from django.core.servers.fastcgi import runfastcgi
 
host = "127.0.0.1"
port = 9876 
 
class django_service(win32serviceutil.ServiceFramework):
    _svc_name_ = "Django Application"
    _svc_display_name_ = "Django Application"
 
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.isAlive = True
        self.isRunning = False
 
    def SvcDoRun(self):
        import servicemanager
        servicemanager.LogInfoMsg("Starting Django Service")
	    while self.isAlive:
	        if not self.isRunning:
		    servicemanager.LogInfoMsg("Starting fcgi server")
                    runfastcgi(protocol="fcgi", method="threaded", daemonize="false", host=host, port=port)
                    self.isRunning = True
 
    def SvcStop(self):
        import servicemanager
        servicemanager.LogInfoMsg("Stopping Django Service")
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
    	self.isAlive = False
 
if __name__ == "__main__":
    win32serviceutil.HandleCommandLine(django_service)

Installing the service is as easy as

C:\foo_bar\django_service.py install

which is why we’ve included the call to HandleCommandLine() in line 35 (running your python script with no options will provide quite some useful help on the other options you have available).

However it’s wrong.

What’s wrong with this?

The code pasted above looks all right but it doesn’t work. Sorry.

Well, it actually does but it has a serious flaw. If you try to stop the service it just won’t stop. This is due to the blocking nature of runfastcgi(daemonize=”false”) so in order to stop/restart it you’ll have to kill the script and start the service again. Definitely not handy!

I have yet to devise how to cleanly stop/restart the service and as I’m not Windows’ biggest fan I can foresee that this will take me quite a bit … until then, stay tuned.

Post a Comment

Your email is never published nor shared. Required fields are marked *