7

我有一个用 Python 编写的小型 Web 服务器应用程序,它从数据库系统获取一些数据并将其作为 XML 返回给用户。这部分工作正常——我可以从命令行运行 Python Web 服务器应用程序,我可以让客户端连接到它并取回数据。目前,要运行 Web 服务器,我必须以管理员用户身份登录到我们的服务器,并且我必须手动启动 Web 服务器。我想让 Web 服务器在系统作为服务启动时自动启动并在后台运行。

使用ActiveState的站点和StackOverflow中的代码,我对如何创建服务有了一个很好的了解,我想我已经整理好了——我可以将我的 Web 服务器作为 Windows 服务安装和启动。但是,我无法弄清楚如何再次停止该服务。我的 Web 服务器是从 BaseHTTPServer 创建的:

server = BaseHTTPServer.HTTPServer(('', 8081), SIMSAPIServerHandler)
server.serve_forever()

serve_forever() 调用自然而然地使 Web 服务器处于无限循环中并等待 HTTP 连接(或 ctrl-break 按键,对服务无用)。我从上面的示例代码中了解到,您的 main() 函数应该处于无限循环中,并且只有在遇到“停止”条件时才退出它。我主要调用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 的方法,以便它停止整个服务。

这是一个完整的列表(我已经修剪了包含/评论以节省空间):

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)
4

1 回答 1

4

这就是我所做的:

我没有直接实例化 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 是实现网络服务器的 AppHTTPServer() 的实例)

如果您在后台线程上正确使用 setDaemon(),并正确中断服务中的所有循环,则该指令

exit(0)

在 SvcStop() 中不应该是必要的

于 2008-09-30T19:47:49.903 回答