Esecuzione di un server Web Python come servizio in Windows
Domanda
Ho una piccola applicazione web server che ho scritto in Python che va e prende alcuni dati da un sistema di database e li restituisce all'utente come XML. Quella parte funziona bene: posso eseguire l'applicazione web server Python dalla riga di comando e posso avere i client connettersi ad essa e recuperare i dati. Al momento, per eseguire il web server devo aver effettuato l'accesso al nostro server come utente amministratore e devo avviare manualmente il web server. Voglio che il server Web si avvii automaticamente all'avvio del sistema come servizio e venga eseguito in background.
Utilizzo del codice dal ActiveState e dal sito StackOverflow , ho una buona idea di come andare sulla creazione di un servizio e penso di aver risolto il problema: posso installare e avviare il mio server Web come servizio Windows. Tuttavia, non riesco a capire come interrompere nuovamente il servizio. Il mio server web è creato da un BaseHTTPServer:
server = BaseHTTPServer.HTTPServer(('', 8081), SIMSAPIServerHandler)
server.serve_forever()
La chiamata serve_forever (), naturalmente, fa sedere il web server in un ciclo infinito e attende connessioni HTTP (o un tasto ctrl-break, non utile per un servizio). Ho preso l'idea dal codice di esempio sopra che la tua funzione main () dovrebbe trovarsi in un ciclo infinito e scoppiare da essa solo quando arriva attraverso un "stop". condizione. Le mie chiamate principali serve_forever (). Ho una funzione SvcStop:
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
exit(0)
Che sembra essere chiamato quando faccio "quotythy myservice stop" dalla riga di comando (posso inserire una riga di debug che produce l'output in un file) ma in realtà non esce dall'intero servizio - le chiamate successive a "python myservice avviano " mi dà un errore:
Errore durante l'avvio del servizio: un'istanza di il servizio è già in esecuzione.
e le successive chiamate a smettere mi danno:
Errore durante l'arresto del servizio: il servizio non posso accettare messaggi di controllo a questo tempo. (1061)
Penso di aver bisogno di qualche sostituto per serve_forever (serve_until_stop_received, o altro) o ho bisogno di un modo per modificare SvcStop in modo che interrompa l'intero servizio.
Ecco un elenco completo (ho tagliato include / commenti per risparmiare spazio):
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)
Soluzione
Questo è quello che faccio:
Invece di installare direttamente la classe BaseHTTPServer.HTTPServer, scrivo da essa un nuovo discendente che pubblica un "stop". Metodo:
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
E poi, nel metodo SvcStop che hai già, chiamo quel metodo per interrompere il ciclo serve_forever ():
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.httpd.stop()
(self.httpd è l'istanza di AppHTTPServer () che implementa il server web)
Se usi setDaemon () correttamente sui thread in background e interrompi correttamente tutti i loop nel servizio, allora l'istruzione
exit(0)
in SvcStop () non dovrebbe essere necessario