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)
È stato utile?

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top