I have a small web server application I've written in Python that goes and gets some data from a database system and returns it to the user as XML. That part works fine - I can run the Python web server application from the command line and I can have clients connect to it and get data back. At the moment, to run the web server I have to be logged in to our server as the administrator user and I have to manually start the web server. I want to have the web server automatically start on system start as a service and run in the background.
Using code from ActiveState's site and StackOverflow, I have a pretty good idea of how to go about creating a service, and I think I've got that bit sorted - I can install and start my web server as a Windows service. I can't, however, figure out how to stop the service again. My web server is created from a BaseHTTPServer:
server = BaseHTTPServer.HTTPServer(('', 8081), SIMSAPIServerHandler)
server.serve_forever()
The serve_forever() call, naturally enough, makes the web server sit in an infinite loop and wait for HTTP connections (or a ctrl-break keypress, not useful for a service). I get the idea from the example code above that your main() function is supposed to sit in an infinite loop and only break out of it when it comes accross a "stop" condition. My main calls serve_forever(). I have a SvcStop function:
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
exit(0)
Which seems to get called when I do "python myservice stop" from the command line (I can put a debug line in there that produces output to a file) but doesn't actually exit the whole service - subsequent calls to "python myservice start" gives me an error:
Error starting service: An instance of the service is already running.
and subsequent calls to stop gives me:
Error stopping service: The service cannot accept control messages at this time. (1061)
I think I need either some replacement for serve_forever (serve_until_stop_received, or whatever) or I need some way of modifying SvcStop so it stops the whole service.
Here's a full listing (I've trimmed includes/comments to save space):
class SIMSAPIServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
try:
reportTuple = self.path.partition("/")
if len(reportTuple) < 3:
return
if reportTuple[2] == "":
return
os.system("C:\\Programs\\SIMSAPI\\runCommandReporter.bat " + reportTuple[2])
f = open("C:\\Programs\\SIMSAPI\\out.xml", "rb")
self.send_response(200)
self.send_header('Content-type', "application/xml")
self.end_headers()
self.wfile.write(f.read())
f.close()
# The output from CommandReporter is simply dumped to out.xml, which we read, write to the user, then remove.
os.unlink("C:\\Programs\\SIMSAPI\\out.xml")
return
except IOError:
self.send_error(404,'File Not Found: %s' % self.path)
class SIMSAPI(win32serviceutil.ServiceFramework):
_svc_name_ = "SIMSAPI"
_svc_display_name_ = "A simple web server"
_svc_description_ = "Serves XML data produced by SIMS CommandReporter"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
exit(0)
def SvcDoRun(self):
import servicemanager
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
self.timeout = 3000
while 1:
server = BaseHTTPServer.HTTPServer(('', 8081), SIMSAPIServerHandler)
server.serve_forever()
def ctrlHandler(ctrlType):
return True
if __name__ == '__main__':
win32api.SetConsoleCtrlHandler(ctrlHandler, True)
win32serviceutil.HandleCommandLine(SIMSAPI)
A webserver in Python can be setup in two ways. Python supports a webserver out of the box. You can start a web server with a one liner. But you can also create a custom web server which has unique functionality.
This is what I do:
Instead of instancing directly the class BaseHTTPServer.HTTPServer, I write a new descendant from it that publishes an "stop" method:
class AppHTTPServer (SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
def serve_forever(self):
self.stop_serving = False
while not self.stop_serving:
self.handle_request()
def stop (self):
self.stop_serving = True
And then, in the method SvcStop that you already have, I call that method to break the serve_forever() loop:
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.httpd.stop()
(self.httpd is the instance of AppHTTPServer() that implements the webserver)
If you use setDaemon() correctly on the background threads, and interrupt correctly all the loops in the service, then the instruction
exit(0)
in SvcStop() should not be necessary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With