在Windows中将Python Web服务器作为服务运行
我有一个用Python编写的小型Web服务器应用程序,可以从数据库系统中获取一些数据,并将其作为XML返回给用户。该部分工作正常,我可以从命令行运行Python Web服务器应用程序,并且可以让客户端连接到该应用程序并取回数据。目前,要运行Web服务器,我必须以管理员用户身份登录到我们的服务器,并且必须手动启动Web服务器。我想让Web服务器作为服务在系统启动时自动启动并在后台运行。
使用ActiveState网站和StackOverflow上的代码,我对如何创建服务有了一个很好的主意,我认为可以将其作为Windows服务安装和启动我的Web服务器了。但是,我无法弄清楚如何再次停止该服务。我的Web服务器是从BaseHTTPServer创建的:
server = BaseHTTPServer.HTTPServer(('', 8081), SIMSAPIServerHandler) server.serve_forever()
自然地,serve_forever()调用使Web服务器处于无限循环中并等待HTTP连接(或者ctrl-break keypress,对服务无用)。我从上面的示例代码中得到的想法是,main()函数应该处于无限循环中,并且仅在遇到"停止"条件时才会中断。我的主要电话是serve_forever()。我有一个SvcStop函数:
def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) exit(0)
当我从命令行执行" python myservice stop"(我可以在其中放置调试行,将输出生成到文件)时,似乎被调用,但实际上并没有退出整个服务,随后对" python myservice start"的调用给我一个错误:
Error starting service: An instance of the service is already running.
随后停止通话给了我:
Error stopping service: The service cannot accept control messages at this time. (1061)
我想我需要替换serve_forever(serve_until_stop_received或者其他),或者需要某种方式修改SvcStop以便停止整个服务。
这是完整的清单(为了节省空间,我已修剪了包含/注释):
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类,而是从中编写了一个新的后代,该后代发布了一个"停止"方法:
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是实现Web服务器的AppHTTPServer()的实例)
如果在后台线程上正确使用setDaemon(),并正确中断服务中的所有循环,则该指令
exit(0)
在SvcStop()中应该没有必要