Запуск веб-сервера Python как службы в Windows
Вопрос
У меня есть небольшое веб-серверное приложение, которое я написал на Python, которое получает некоторые данные из системы баз данных и возвращает их пользователю в виде XML.Эта часть работает нормально - я могу запустить веб-серверное приложение Python из командной строки, и я могу заставить клиентов подключаться к нему и получать данные обратно.На данный момент, чтобы запустить веб-сервер, я должен войти на наш сервер как пользователь-администратор, и я должен вручную запустить веб-сервер.Я хочу, чтобы веб-сервер автоматически запускался при запуске системы как служба и работал в фоновом режиме.
Используя код из Активный статуссайт и StackOverflow ( Стековый поток), У меня есть довольно хорошее представление о том, как приступить к созданию сервиса, и я думаю, что с этим немного разобрался - я могу установить и запустить свой веб-сервер как службу Windows.Однако я не могу понять, как снова остановить службу.Мой веб-сервер создан на основе BaseHTTPServer:
server = BaseHTTPServer.HTTPServer(('', 8081), SIMSAPIServerHandler)
server.serve_forever()
Вызов serve_forever(), естественно, заставляет веб-сервер находиться в бесконечном цикле и ждать HTTP-соединений (или нажатия клавиши ctrl-break, что бесполезно для службы).Из приведенного выше примера кода я понял, что ваша функция main() должна находиться в бесконечном цикле и выходить из него только тогда, когда возникает условие "stop".Мой основной вызов serve_forever().У меня есть функция SvcStop:
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
exit(0)
Который, кажется, вызывается, когда я выполняю "python myservice stop" из командной строки (я могу поместить туда строку отладки, которая выдает вывод в файл), но фактически не завершает работу всей службы - последующие вызовы "python myservice start" выдают мне ошибку:
Ошибка при запуске службы:Экземпляр служба уже запущена.
и последующие призывы остановиться дают мне:
Ошибка при остановке службы:Служба в данный момент не может принимать управляющие сообщения .(1061)
Я думаю, мне нужна либо какая-то замена serve_forever (serve_until_stop_received, или что-то еще), либо мне нужен какой-то способ изменить SvcStop, чтобы он останавливал всю службу.
Вот полный список (я обрезал includes / comments для экономии места):
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)
Решение
Это то, что я делаю:
Вместо непосредственного создания экземпляра класса BaseHTTPServer.HTTPServer я пишу из него новый потомок, который публикует метод "stop":
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
И затем, в методе SvcStop, который у вас уже есть, я вызываю этот метод, чтобы прервать цикл serve_forever():
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.httpd.stop()
(self.httpd - это экземпляр AppHTTPServer(), который реализует веб-сервер)
Если вы правильно используете setDaemon() в фоновых потоках и правильно прерываете все циклы в службе, то инструкция
exit(0)
в SvcStop() не должно быть необходимости