[Twisted-Python] Daemon processes on windows
Brian Granger
ellisonbg.net at gmail.com
Sun Nov 8 20:54:11 MST 2009
David,
While this process is certainly doable, I'll also point out that on
> Windows, the more "natural" approach to this is to implement the
> process as a service. That also buys you some regular Windows
> approaches to management (net stop/start from command line, services
> UI from the control panel) and status inquiry (sc query from command
> line). So while it may be a little platform-specific code you're
> final result will integrate more naturally into that environment for
> any system administrators.
>
>
It is very possible I might have to go this route. We have some odd
requrements,
such as needing to run the server using the Windows Server 2008 HPC job
scheduler.
the job scheduler does some funky things and I am finding that some of the
"usual"
assumptions you make on Windows don't hold. So at this point, I need all
the tricks
in my bad that I can get. Thanks for the example!
> While I haven't done this with a twistd based Twisted service, I've
> done it with a lot of Twisted code in general. It's easiest if you have
> pywin32 installed and can use it's utility service wrappers, for which
> the pywin32 package has examples.
>
OK, nice to know about these examples.
> I've tended to offload the service definition to its own module, and
> have it just import and execute the main code (with the twisted
> reactor.run call) after the service gets started - that way you can
> also manually run the twisted code without involving any of the
> service support, or to support execution on non-Windows platforms.
>
> Here's one example of a service object that invoices Twisted-based
> code. When stopping, you use callFromThread because the thread that
> handles the request from the Windows service manager is separate from
> the thread executing the main code (which is a thread created during
> the startup process).
>
> That's also why you start the reactor without signal initialization,
> since it'll be in a secondary thread. In SvcDoRun is where you could
> import platform-generic modules to set up your twisted code, and then
> this code starts the reactor.
>
Bummer, then I can't use this approach. My "server" uses
reactor.spawnProcess
which needs the signal handlers to be installed (SIGCHLD specifically) to
work
properly... do you know if it can be done without the dual thread trick.
Thanks,
Brian
Oh, and I left some code in that shows retrieving service-specific
> parameters from the registry as is typical for Windows services
> (stored in HKLM/System/CurrentControlSet/Services/<svc_name>/Parameters
> if I recall corrrectly):
>
> - - - - -
>
> class DataImportService(win32serviceutil.ServiceFramework):
>
> __version__ = '1.0.0'
>
> _svc_name_ = 'fdi'
> _svc_display_name_ = 'SomeCompany Data Integration'
>
> stopping = False
> debug = False
>
> def __init__(self, *args, **kwargs):
> win32serviceutil.ServiceFramework.__init__(self, *args, **kwargs)
> self.initialize()
> self._stopped = threading.Event()
> self.log = None
>
> def initialize(self):
> # This is separate from __init__ so during debugging the bootstrap
> # code can override __init__ but still finish initialization.
>
> def _getOption(self, option, default):
> return win32serviceutil.GetServiceCustomOption(self._svc_name_,
> option,
> default)
>
> def SvcStop(self):
> self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
> print '%s Service stopping' % self._svc_display_name_
> reactor.callFromThread(reactor.stop)
> self._stopped.wait(5)
> print '%s Service stopped' % self._svc_display_name_
> if self.log:
> self.log.close()
>
> def SvcDoRun(self):
> """Main entry point for service execution"""
>
> if hasattr(sys, 'frozen'):
> home_dir = os.path.dirname(sys.executable)
> else:
> home_dir = os.path.dirname(__file__)
>
> # First, let's set up some logging
> if self.debug:
> dupstdout = True
> logprefix = None
> else:
> dupstdout = False
> logprefix = os.path.join(home_dir, 'logs', 'dataimport.log')
>
> self.log = LogClass(logprefix, dupstdout)
>
> # And then reroute stdout/stderr to that file object
> sys.stdout = self.log
> sys.stderr = self.log
>
> print '%s Service %s starting' % (self._svc_display_name_,
> self.__version__)
>
> try:
>
> # Process config file
> config_file = self._getOption('config',
> os.path.join(home_dir, 'data',
> 'dataimport.ini'))
>
> # ... other service-related initialization ...
>
> # ... do any Twisted related initialization ...
>
> # Start everything up
> reactor.callLater(0, self.log.write,
> '%s Service operational\n' %
> self._svc_display_name_)
> reactor.run(installSignalHandlers=False)
>
> # We're shutting down.
>
> # ... do any shutdown processing ...
>
> # Flag that we're exiting so service thread can be more
> # accurate in terms of declaring shutdown.
> self._stopped.set()
>
> except:
>
> # For right now just log a traceback and abort
> log.err()
> # But try to gracefully close down log to let final traceback
> # information make it out to the log file.
> if self.log:
> self.log.close()
>
> - - - - -
>
> Your main startup code in the service wrapper module can use helper
> functions from win32serviceutil for startup (see the pywin32 examples)
> which provides some automatic support for installing/uninstalling the
> service, or you can implement your own startup if you want to support
> different options and what not. This all works under py2exe as well
> if you want to package things up and have a nice self-contained exe as
> a Windows service.
>
> -- David
>
>
> _______________________________________________
> Twisted-Python mailing list
> Twisted-Python at twistedmatrix.com
> http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20091108/985180fe/attachment.html>
More information about the Twisted-Python
mailing list